From a25a2dd38972988268bbfbd710bc1b861b2a007b Mon Sep 17 00:00:00 2001 From: steffen123 Date: Wed, 7 Jul 2010 09:46:16 +0200 Subject: [PATCH 01/80] some improvements on the gentoo ebuild --- packaging/gentoo/fpdb-0.20-r1.ebuild | 57 +++++++++++++++++++++++++++ packaging/gentoo/fpdb-0.20_rc1.ebuild | 53 ------------------------- 2 files changed, 57 insertions(+), 53 deletions(-) create mode 100644 packaging/gentoo/fpdb-0.20-r1.ebuild delete mode 100644 packaging/gentoo/fpdb-0.20_rc1.ebuild 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!" -} From aeaac92fb958d26cc207729c0de2a0058154d942 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Wed, 7 Jul 2010 22:21:46 +0200 Subject: [PATCH 02/80] commit before changing intendation. --- pyfpdb/ImapSummaries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/ImapSummaries.py b/pyfpdb/ImapSummaries.py index 50079488..8482478f 100755 --- a/pyfpdb/ImapSummaries.py +++ b/pyfpdb/ImapSummaries.py @@ -68,7 +68,7 @@ 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") + result=PokerStarsSummary.PokerStarsSummary(db=db, sitename="PokerStars", summaryText=summaryText, builtFrom = "IMAP") #print "result:",result #TODO: count results and output to shell like hand importer does From c6b6f8a788a3f298d8d45365732e0a429bb9af5e Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 19:46:25 +0200 Subject: [PATCH 03/80] many not really separable changes for IMAP import and associated cleaning - deactivated use of TourneySummary (short: TS) from HHC and subclasses - menu entry for IMAP import - converted ImapSummaries (short: IS) to use the config file - IS now optionally supports non-SSL and can run on folders other than INBOX - removed gametypes and getGameTypeAsString from TS method as identical one is in Hand - some other stuff --- pyfpdb/Configuration.py | 17 ++++++++++++++ pyfpdb/Database.py | 18 ++++++++------ pyfpdb/FulltiltToFpdb.py | 2 +- pyfpdb/Hand.py | 1 - pyfpdb/HandHistoryConverter.py | 1 - pyfpdb/ImapSummaries.py | 43 +++++++++++++++++----------------- pyfpdb/PokerStarsSummary.py | 21 ++++++++++------- pyfpdb/SQL.py | 1 - pyfpdb/fpdb.pyw | 10 +++++++- 9 files changed, 72 insertions(+), 42 deletions(-) diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 6a71a750..ac0229f6 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/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..4c4cf27a 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -802,17 +802,17 @@ 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)) + playerNameUtf = Charset.to_utf8(playerName) + print "db.get_player_id siteName",siteName,"playerName",playerName + c.execute(self.sql.query['get_player_id'], (playerNameUtf, siteName)) 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""" @@ -1995,10 +1995,14 @@ class Database: return tourneyId #end def createOrUpdateTourney - def createOrUpdateTourneysPlayers(self, hand): + def createOrUpdateTourneysPlayers(self, hand, source=None): tourneysPlayersIds=[] for player in hand.players: - playerId = hand.dbid_pids[player[1]] + print "beginning of for in createOrUpdateTourneysPlayers, player",player,"dbid_pids",hand.dbid_pids + if source=="TourneySummary": #TODO remove this horrible hack + playerId = hand.dbid_pids[player] + else: + playerId = hand.dbid_pids[player[1]] cursor = self.get_cursor() cursor.execute (self.sql.query['getTourneysPlayersId'].replace('%s', self.sql.query['placeholder']), diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 9b287edc..afa7b622 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -20,7 +20,7 @@ import logging from HandHistoryConverter import * -import TourneySummary +#import TourneySummary # Fulltilt HH Format converter diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index a2cd8b0e..f26258ff 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -37,7 +37,6 @@ import Configuration from Exceptions import * import DerivedStats import Card -import Tourney class Hand(object): diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 7d04e824..3f3d2bd7 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -35,7 +35,6 @@ log = logging.getLogger("parser") import Hand -import Tourney from Exceptions import FpdbParseError import Configuration diff --git a/pyfpdb/ImapSummaries.py b/pyfpdb/ImapSummaries.py index 8482478f..844d4b3b 100755 --- a/pyfpdb/ImapSummaries.py +++ b/pyfpdb/ImapSummaries.py @@ -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 "in try of IS.run" + 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(db=db, sitename="PokerStars", 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 - server.logout() + #finally: + # try: + # server.close() + # finally: + # pass + #server.logout() \ No newline at end of file diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index 1f8042af..2000d7f2 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -23,16 +23,11 @@ from PokerStarsToFpdb import PokerStars from TourneySummary import * class PokerStarsSummary(TourneySummary): - sitename = "PokerStars" - siteId = 2 - #limits = PokerStars.limits - #games = PokerStars.games - # = PokerStars. - 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_Player = re.compile(u"""(?P[0-9]+):\s(?P.*)\s\(.*\),(\s\$(?P[0-9]+\.[0-9]+)\s\()?""") + re_BuyInFee = re.compile("(?P[0-9]+\.[0-9]+).*(?P[0-9]+\.[0-9]+)") # = re.compile("") def parseSummary(self): @@ -40,7 +35,17 @@ class PokerStarsSummary(TourneySummary): 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 + if lines[1].find("$")!=-1: + self.currency="USD" + elif lines[1].find(u"€")!=-1: + self.currency="EUR" + else: + raise fpdbParseError("didn't recognise buyin currency") + + result=self.re_BuyInFee.search(lines[1]) + result=result.groupdict() + self.buyin=int(100*Decimal(result['BUYIN'])) + self.fee=int(100*Decimal(result['FEE'])) self.entries = self.re_Entries.findall(lines[2])[0] diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 97cc89cc..1c526717 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1944,7 +1944,6 @@ class Sql: self.query['getPlayerIdBySite'] = """SELECT id from Players where name = %s AND siteId = %s""" - # used in Filters: self.query['getSiteId'] = """SELECT id from Sites where name = %s""" self.query['getGames'] = """SELECT DISTINCT category from Gametypes""" diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index fd7cda0f..e5f09c26 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -102,6 +102,7 @@ import GuiPrefs import GuiLogView import GuiDatabase import GuiBulkImport +import ImapSummaries import GuiPlayerStats import GuiPositionalStats import GuiTableViewer @@ -655,6 +656,7 @@ class fpdb: + @@ -701,6 +703,7 @@ class fpdb: ('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), + ('imapsummaries', None, '_Import Tourney Summaries through eMail/IMAP', 'I', 'Auto Import and HUD', self.import_imap_summaries), ('autorate', None, 'Auto _Rating (todo)', 'R', 'Auto Rating (todo)', self.not_implemented), ('viewers', None, '_Viewers'), ('autoimp', None, '_Auto Import and HUD', 'A', 'Auto Import and HUD', self.tab_auto_import), @@ -734,6 +737,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.""" @@ -879,7 +888,6 @@ 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() From 10cfaf2c750cad1681ff81db371849b41b36bb64 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 20:00:43 +0200 Subject: [PATCH 04/80] missed file for last commit --- pyfpdb/TourneySummary.py | 90 +++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 52 deletions(-) diff --git a/pyfpdb/TourneySummary.py b/pyfpdb/TourneySummary.py index 20d68c4a..8c6c6c47 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 @@ -92,6 +93,8 @@ class TourneySummary(object): self.guarantee = 0 # Collections indexed by player names + self.playerIds = {} + self.tourneysPlayersIds = {} self.ranks = {} self.winnings = {} self.winningsCurrency = {} @@ -101,16 +104,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 +121,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), @@ -148,8 +151,9 @@ 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), ("COUNT REBUYS", self.rebuyCounts), @@ -171,9 +175,7 @@ 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 +186,28 @@ 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: + self.db.insertPlayer(player, self.siteId) + id=self.db.get_last_insert_id(self.db.cursor) + + 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) + self.db.commit() + self.tourneysPlayersIds = self.db.createOrUpdateTourneysPlayers(self, "TourneySummary") + 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) @@ -240,35 +255,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 +266,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 +302,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]) From 178b528d3b3ba40c4612577f6a66cd358ac7eb7a Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 20:01:03 +0200 Subject: [PATCH 05/80] change calls to python2 back to python --- pyfpdb/AbsoluteToFpdb.py | 2 +- pyfpdb/AlchemyFacilities.py | 2 +- pyfpdb/AlchemyMappings.py | 2 +- pyfpdb/AlchemyTables.py | 2 +- pyfpdb/Anonymise.py | 2 +- pyfpdb/BetfairToFpdb.py | 2 +- pyfpdb/CarbonToFpdb.py | 2 +- pyfpdb/Card.py | 2 +- pyfpdb/Charset.py | 2 +- pyfpdb/Configuration.py | 2 +- pyfpdb/Database.py | 2 +- pyfpdb/DerivedStats.py | 2 +- pyfpdb/EverleafToFpdb.py | 2 +- pyfpdb/Exceptions.py | 2 +- pyfpdb/Filters.py | 2 +- pyfpdb/FpdbSQLQueries.py | 2 +- pyfpdb/FulltiltToFpdb.py | 2 +- pyfpdb/GuiAutoImport.py | 2 +- pyfpdb/GuiBulkImport.py | 2 +- pyfpdb/GuiDatabase.py | 2 +- pyfpdb/GuiGraphViewer.py | 2 +- pyfpdb/GuiLogView.py | 2 +- pyfpdb/GuiPlayerStats.py | 2 +- pyfpdb/GuiPositionalStats.py | 2 +- pyfpdb/GuiPrefs.py | 2 +- pyfpdb/GuiSessionViewer.py | 2 +- pyfpdb/GuiTableViewer.py | 2 +- pyfpdb/HUD_main.pyw | 2 +- pyfpdb/HUD_run_me.py | 2 +- pyfpdb/Hand.py | 2 +- pyfpdb/HandHistory.py | 2 +- pyfpdb/HandHistoryConverter.py | 2 +- pyfpdb/Hello.py | 2 +- pyfpdb/Hud.py | 2 +- pyfpdb/ImapSummaries.py | 2 +- pyfpdb/Mucked.py | 2 +- pyfpdb/OnGameToFpdb.py | 2 +- pyfpdb/Options.py | 2 +- pyfpdb/PartyPokerToFpdb.py | 2 +- pyfpdb/PokerStarsSummary.py | 2 +- pyfpdb/PokerStarsToFpdb.py | 2 +- pyfpdb/SQL.py | 2 +- pyfpdb/Stats.py | 2 +- pyfpdb/Summaries.py | 2 +- pyfpdb/SummaryEverleaf.py | 2 +- pyfpdb/TableWindow.py | 2 +- pyfpdb/Tables.py | 2 +- pyfpdb/Tables_Demo.py | 2 +- pyfpdb/TournamentTracker.py | 2 +- pyfpdb/UltimateBetToFpdb.py | 2 +- pyfpdb/Win2dayToFpdb.py | 2 +- pyfpdb/WinTables.py | 2 +- pyfpdb/XTables.py | 2 +- pyfpdb/fpdb.pyw | 2 +- pyfpdb/fpdb_import.py | 2 +- pyfpdb/makeexe.py | 2 +- pyfpdb/py2exe_setup.py | 2 +- pyfpdb/test_Betfair.py | 2 +- pyfpdb/test_Database.py | 2 +- pyfpdb/test_Everleaf.py | 2 +- pyfpdb/test_FullTilt.py | 2 +- pyfpdb/test_PokerStars.py | 2 +- pyfpdb/windows_make_bats.py | 2 +- regression-test/PrintHand.py | 2 +- regression-test/PrintPlayerHudData.py | 2 +- regression-test/fpdb_util_lib.py | 2 +- run_fpdb.py | 2 +- setup.py | 2 +- test_Python.py | 2 +- test_Python_Libs.py | 2 +- utils/fix_table_desc.py | 2 +- 71 files changed, 71 insertions(+), 71 deletions(-) diff --git a/pyfpdb/AbsoluteToFpdb.py b/pyfpdb/AbsoluteToFpdb.py index 03266bc8..46157fee 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..f4884de3 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 diff --git a/pyfpdb/AlchemyTables.py b/pyfpdb/AlchemyTables.py index f919fd09..61feb0d5 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 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 80fdfb11..7bacd34b 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 32ad2ef8..0000f9f8 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 ac0229f6..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 diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 4c4cf27a..9f5735a1 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 diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index ee86a689..55e3a01c 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 diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index ee6b3601..54291a25 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..24981f65 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index fb67f560..cb131f51 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index afa7b622..0a808cf4 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 diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index f3493ac8..ba6d4ee8 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..a25db1d2 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 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..96026864 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 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..12bfefd1 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/pyfpdb/GuiPositionalStats.py b/pyfpdb/GuiPositionalStats.py index 3dd32c98..724d0f84 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 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/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index ed4e9eff..05249051 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 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/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 f26258ff..ac4835a5 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 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 3f3d2bd7..acabbd8b 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 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 844d4b3b..23fda399 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 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 c560790d..158e7f08 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 1f804d2a..fc8379e9 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 2000d7f2..ada27448 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 diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 33e68d5a..78808317 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 diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 1c526717..bceefa1e 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. """ 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 6f9ded41..dd2958bd 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/UltimateBetToFpdb.py b/pyfpdb/UltimateBetToFpdb.py index 1795ac87..f0b764d3 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 9a1d40eb..3da0ef52 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 e5f09c26..54404642 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 diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index d777dec8..e8455c01 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 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 74c866a9..c87f2e6e 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 b680edbc..5f5b244c 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 From 7070c1431ae852d1d4e6b3f580729e1a6dba7bc2 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 21:28:21 +0200 Subject: [PATCH 06/80] put timezone removal into separate method for use by PSS --- pyfpdb/PokerStarsToFpdb.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 78808317..23de7565 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -227,17 +227,7 @@ class PokerStars(HandHistoryConverter): #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)) + self.removeET(hand) if key == 'HID': hand.handid = info[key] if key == 'TOURNO': @@ -277,7 +267,21 @@ class PokerStars(HandHistoryConverter): if key == 'PLAY' and info['PLAY'] is not None: # hand.currency = 'play' # overrides previously set value hand.gametype['currency'] = 'play' - + + def removeET(self, hand): + # 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)) + #end def removeET + def readButton(self, hand): m = self.re_Button.search(hand.handText) if m: From 201b3a5b6c41a902e90a86a4af4cad3cadf83b8b Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 21:41:27 +0200 Subject: [PATCH 07/80] PS Summary now parses start/endTime. fixes MySQL support also removed some but not all the new prints --- pyfpdb/Database.py | 3 +-- pyfpdb/PokerStarsSummary.py | 12 +++++++++++- pyfpdb/TourneySummary.py | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 9f5735a1..c60a8023 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -805,7 +805,7 @@ class Database: def get_player_id(self, config, siteName, playerName): c = self.connection.cursor() playerNameUtf = Charset.to_utf8(playerName) - print "db.get_player_id siteName",siteName,"playerName",playerName + #print "db.get_player_id siteName",siteName,"playerName",playerName c.execute(self.sql.query['get_player_id'], (playerNameUtf, siteName)) row = c.fetchone() if row: @@ -1998,7 +1998,6 @@ class Database: def createOrUpdateTourneysPlayers(self, hand, source=None): tourneysPlayersIds=[] for player in hand.players: - print "beginning of for in createOrUpdateTourneysPlayers, player",player,"dbid_pids",hand.dbid_pids if source=="TourneySummary": #TODO remove this horrible hack playerId = hand.dbid_pids[player] else: diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index ada27448..40b235d0 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -18,6 +18,7 @@ """pokerstars-specific summary parsing code""" from decimal import Decimal +import datetime from PokerStarsToFpdb import PokerStars from TourneySummary import * @@ -28,6 +29,7 @@ class PokerStarsSummary(TourneySummary): re_Prizepool = re.compile("\$[0-9]+\.[0-9]+") re_Player = re.compile(u"""(?P[0-9]+):\s(?P.*)\s\(.*\),(\s\$(?P[0-9]+\.[0-9]+)\s\()?""") re_BuyInFee = re.compile("(?P[0-9]+\.[0-9]+).*(?P[0-9]+\.[0-9]+)") + 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.compile("") def parseSummary(self): @@ -52,7 +54,15 @@ class PokerStarsSummary(TourneySummary): self.prizepool = self.re_Prizepool.findall(lines[3])[0] self.prizepool = self.prizepool[1:-3]+self.prizepool[-2:] - #TODO: lines 4 and 5 are dates, read them + result=self.re_DateTime.search(lines[4]) + 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" + + result=self.re_DateTime.search(lines[5]) + 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" for i in range(6,len(lines)-2): #lines with rank and winnings info if lines[i].find(":")==-1: diff --git a/pyfpdb/TourneySummary.py b/pyfpdb/TourneySummary.py index 8c6c6c47..032a04e6 100644 --- a/pyfpdb/TourneySummary.py +++ b/pyfpdb/TourneySummary.py @@ -194,7 +194,7 @@ class TourneySummary(object): self.playerIds.update({player:id}) - print "TS.insert players",self.players,"playerIds",self.playerIds + #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 From f2fcceba1728d11f1a48c5b66cf06074d63ddcb4 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 21:46:40 +0200 Subject: [PATCH 08/80] remove more prints --- pyfpdb/ImapSummaries.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyfpdb/ImapSummaries.py b/pyfpdb/ImapSummaries.py index 23fda399..3e17d068 100755 --- a/pyfpdb/ImapSummaries.py +++ b/pyfpdb/ImapSummaries.py @@ -30,10 +30,9 @@ def splitPokerStarsSummaries(emailText): #end def emailText def run(config, db): - print "start of IS.run" + #print "start of IS.run" server=None #try: - print "in try of IS.run" if config.email.useSsl: server = IMAP4_SSL(config.email.host) else: From f0b3dcb7a8f88fc03c0f25a4b0f5a4fd47c406d1 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Wed, 16 Jun 2010 08:16:06 +0300 Subject: [PATCH 09/80] Update debian packaging Changelog bump for new snapshot, and executable script renames that were done for win32 setups. (cherry picked from commit a5a507ece798847b67dbc6f6515e1311a3f53961) Signed-off-by: steffen123 --- packaging/debian/changelog | 9 +++++++++ packaging/debian/links | 2 +- packaging/debian/python-fpdb.postinst | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packaging/debian/changelog b/packaging/debian/changelog index ebd45a53..e482e349 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -1,3 +1,12 @@ +free-poker-tools (0.20~git20100616) unstable; urgency=low + + * Snapshot release + * Executable names have been changed to accommodate for some win32 + trouble cases; update paths + * Improved statistics + + -- Mika Bostrom Wed, 16 Jun 2010 08:10:00 +0300 + free-poker-tools (0.20~git20100305) unstable; urgency=low * New snapshot diff --git a/packaging/debian/links b/packaging/debian/links index 5a4601a7..148aad4f 100644 --- a/packaging/debian/links +++ b/packaging/debian/links @@ -1 +1 @@ -/usr/share/pyshared/fpdb/fpdb.py /usr/bin/fpdb +/usr/share/pyshared/fpdb/fpdb.pyw /usr/bin/fpdb diff --git a/packaging/debian/python-fpdb.postinst b/packaging/debian/python-fpdb.postinst index 9680be90..99d25018 100644 --- a/packaging/debian/python-fpdb.postinst +++ b/packaging/debian/python-fpdb.postinst @@ -3,4 +3,4 @@ # When installed into .../fpdb/ the script gets mode 644 # Note: "dh_fixperms -Xfpdb.py" did not work, hence this hack chmod 755 /usr/bin/fpdb -chmod 755 /usr/share/pyshared/fpdb/HUD_main.py +chmod 755 /usr/share/pyshared/fpdb/HUD_main.pyw From d90fa27274de4368782dd044e55d34665e154d98 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 22:47:37 +0200 Subject: [PATCH 10/80] fix starttime to startTime in my new method --- pyfpdb/PokerStarsToFpdb.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 95c0b043..e85ee191 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -270,15 +270,15 @@ class PokerStars(HandHistoryConverter): def removeET(self, hand): # 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 + 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 + hand.startTime = hand.startTime + offset #print " tz = %s start = %s" % (tz, str(hand.starttime)) #end def removeET From ce0d3f59f6bfb394e7017d6f6e76152bfd9e5fa1 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 23:31:39 +0200 Subject: [PATCH 11/80] correct for timezone in PS summaries, too --- pyfpdb/ImapSummaries.py | 1 + pyfpdb/PokerStarsSummary.py | 3 ++- pyfpdb/PokerStarsToFpdb.py | 31 ++++++++++++++++--------------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/pyfpdb/ImapSummaries.py b/pyfpdb/ImapSummaries.py index 3e17d068..4f8813dd 100755 --- a/pyfpdb/ImapSummaries.py +++ b/pyfpdb/ImapSummaries.py @@ -33,6 +33,7 @@ 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: diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index 40b235d0..6640daa9 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -20,7 +20,7 @@ from decimal import Decimal import datetime -from PokerStarsToFpdb import PokerStars +import PokerStarsToFpdb from TourneySummary import * class PokerStarsSummary(TourneySummary): @@ -58,6 +58,7 @@ class PokerStarsSummary(TourneySummary): 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 = PokerStarsToFpdb.removeET(self.startTime) result=self.re_DateTime.search(lines[5]) result=result.groupdict() diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index e85ee191..2beeb286 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -26,6 +26,21 @@ from decimal import Decimal # PokerStars HH Format +def removeET(time): + # 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 def removeET + class PokerStars(HandHistoryConverter): # Class Variables @@ -227,7 +242,7 @@ class PokerStars(HandHistoryConverter): #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" - self.removeET(hand) + hand.startTime = removeET(hand.startTime) if key == 'HID': hand.handid = info[key] if key == 'TOURNO': @@ -268,20 +283,6 @@ class PokerStars(HandHistoryConverter): # hand.currency = 'play' # overrides previously set value hand.gametype['currency'] = 'play' - def removeET(self, hand): - # 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)) - #end def removeET - def readButton(self, hand): m = self.re_Button.search(hand.handText) if m: From e815cc38db00a0171e0701af386548ebe2201376 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 23:43:43 +0200 Subject: [PATCH 12/80] change version to 0.21 --- pyfpdb/fpdb.pyw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index e6948222..b5a2affc 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -114,7 +114,7 @@ import Database import Configuration import Exceptions -VERSION = "0.20" +VERSION = "0.21" class fpdb: From 5b64ad771a4a8cb4c20151317b1a40119d13fa5e Mon Sep 17 00:00:00 2001 From: steffen123 Date: Thu, 8 Jul 2010 23:46:54 +0200 Subject: [PATCH 13/80] it now stores entries, prizepool and endTime into TT --- pyfpdb/Database.py | 24 ++++++++++++++++-------- pyfpdb/Hand.py | 4 ++-- pyfpdb/TourneySummary.py | 6 ++++-- pyfpdb/fpdb.pyw | 2 +- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index c60a8023..eb6f31a7 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -1932,7 +1932,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 @@ -1978,7 +1978,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,21 +1987,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, source=None): + def createOrUpdateTourneysPlayers(self, hand, source):#note: this method is used on Hand and TourneySummary objects tourneysPlayersIds=[] for player in hand.players: - if source=="TourneySummary": #TODO remove this horrible hack + if source=="TS": #TODO remove this horrible hack playerId = hand.dbid_pids[player] - else: + 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']), diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index ac4835a5..59311ae5 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -229,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 diff --git a/pyfpdb/TourneySummary.py b/pyfpdb/TourneySummary.py index 032a04e6..9d7c3009 100644 --- a/pyfpdb/TourneySummary.py +++ b/pyfpdb/TourneySummary.py @@ -76,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 @@ -133,6 +134,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), @@ -202,9 +204,9 @@ class TourneySummary(object): #print "TS.self before starting insert",self self.tourneyTypeId = self.db.createOrUpdateTourneyType(self) self.db.commit() - self.tourneyId = self.db.createOrUpdateTourney(self) + self.tourneyId = self.db.createOrUpdateTourney(self, "TS") self.db.commit() - self.tourneysPlayersIds = self.db.createOrUpdateTourneysPlayers(self, "TourneySummary") + self.tourneysPlayersIds = self.db.createOrUpdateTourneysPlayers(self, "TS") self.db.commit() logging.debug("Tourney Insert/Update done") diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index b5a2affc..5a4f8e58 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -741,7 +741,7 @@ class fpdb: def import_imap_summaries(self, widget, data=None): result=ImapSummaries.run(self.config, self.db) - print "import imap summaries result:", result + #print "import imap summaries result:", result #end def import_imap_summaries def load_profile(self, create_db = False): From 310c7a2947c9b9c4a3e3cb0bcd9a0e482989f8da Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 01:07:22 +0200 Subject: [PATCH 14/80] PS Summary now stores rank, winnings, etc. into TourneysPl. --- pyfpdb/Database.py | 10 ++++++++-- pyfpdb/SQL.py | 4 ++-- pyfpdb/TourneySummary.py | 13 ++++++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index eb6f31a7..2a12ed11 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -2019,8 +2019,14 @@ 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] + 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])) tourneysPlayersIds.append(self.get_last_insert_id(cursor)) return tourneysPlayersIds #end def createOrUpdateTourneysPlayers diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index bceefa1e..9ad9f321 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -3664,8 +3664,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 diff --git a/pyfpdb/TourneySummary.py b/pyfpdb/TourneySummary.py index 9d7c3009..f7632ea3 100644 --- a/pyfpdb/TourneySummary.py +++ b/pyfpdb/TourneySummary.py @@ -158,6 +158,7 @@ class TourneySummary(object): ("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) @@ -191,9 +192,7 @@ class TourneySummary(object): for player in self.players: id=self.db.get_player_id(self.config, self.siteName, player) if not id: - self.db.insertPlayer(player, self.siteId) - id=self.db.get_last_insert_id(self.db.cursor) - + id=self.db.insertPlayer(player, self.siteId) self.playerIds.update({player:id}) #print "TS.insert players",self.players,"playerIds",self.playerIds @@ -236,10 +235,18 @@ winnings (decimal) the money the player ended the tourney with (can be 0, or self.winningsCurrency.update( { name : winningsCurrency } ) 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): From 9cb47b4195f9ede056ae21326302044692425738 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 01:10:06 +0200 Subject: [PATCH 15/80] stop it from dying on fpdb summary files (by commenting out the whole section) --- pyfpdb/FulltiltToFpdb.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 06a1a32f..d9e924f0 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -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 From a97342ec2e16b8592324088c0d7df46728a01222 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 01:20:31 +0200 Subject: [PATCH 16/80] expand config example with the new email section --- pyfpdb/HUD_config.xml.example | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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" + + + - + From d138c3b4e1cb87c4243134156cdcddeb2890cc3a Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 04:47:33 +0200 Subject: [PATCH 17/80] create subclasses for guiplayerstats and filters --- pyfpdb/GuiRingPlayerStats.py | 36 ++++++++++++++++++++++++++++ pyfpdb/GuiTourneyPlayerStats.py | 36 ++++++++++++++++++++++++++++ pyfpdb/RingFilters.py | 42 +++++++++++++++++++++++++++++++++ pyfpdb/TourneyFilters.py | 42 +++++++++++++++++++++++++++++++++ pyfpdb/fpdb.pyw | 21 ++++++++++++----- 5 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 pyfpdb/GuiRingPlayerStats.py create mode 100644 pyfpdb/GuiTourneyPlayerStats.py create mode 100644 pyfpdb/RingFilters.py create mode 100644 pyfpdb/TourneyFilters.py diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py new file mode 100644 index 00000000..11e8a1b1 --- /dev/null +++ b/pyfpdb/GuiRingPlayerStats.py @@ -0,0 +1,36 @@ +#!/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 RingFilters +import GuiPlayerStats +#import Charset + +class GuiRingPlayerStats (GuiPlayerStats.GuiPlayerStats): + pass +#end class GuiRingPlayerStats \ No newline at end of file diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py new file mode 100644 index 00000000..585b67f2 --- /dev/null +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -0,0 +1,36 @@ +#!/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 TourneyFilters +import GuiPlayerStats +#import Charset + +class GuiTourneyPlayerStats (GuiPlayerStats.GuiPlayerStats): + pass +#end class GuiTourneyPlayerStats \ No newline at end of file diff --git a/pyfpdb/RingFilters.py b/pyfpdb/RingFilters.py new file mode 100644 index 00000000..4780b131 --- /dev/null +++ b/pyfpdb/RingFilters.py @@ -0,0 +1,42 @@ +#!/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 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 +import Filters + +class RingFilters(Filters.Filters): + pass +#end class RingFilters \ No newline at end of file diff --git a/pyfpdb/TourneyFilters.py b/pyfpdb/TourneyFilters.py new file mode 100644 index 00000000..dcddba82 --- /dev/null +++ b/pyfpdb/TourneyFilters.py @@ -0,0 +1,42 @@ +#!/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 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 +import Filters + +class TourneyFilters(Filters.Filters): + pass +#end class TourneyFilters \ No newline at end of file diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 5a4f8e58..e2e3f54e 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -103,7 +103,8 @@ import GuiLogView import GuiDatabase import GuiBulkImport import ImapSummaries -import GuiPlayerStats +import GuiRingPlayerStats +import GuiTourneyPlayerStats import GuiPositionalStats import GuiTableViewer import GuiAutoImport @@ -665,7 +666,8 @@ class fpdb: - + + @@ -710,7 +712,8 @@ class fpdb: ('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), @@ -893,11 +896,17 @@ class fpdb: 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.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) From a724102dbcb7ec026fd2d1ca89190d1d90c22b9c Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 20:29:44 +0200 Subject: [PATCH 18/80] remove unimplemented edit profile menu entry --- pyfpdb/fpdb.pyw | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index e2e3f54e..094024e3 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -335,11 +335,6 @@ class fpdb: self.obtain_global_lock() self.release_global_lock() - 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() @@ -648,7 +643,6 @@ class fpdb: - @@ -699,7 +693,6 @@ 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'), From 36b90ba654f898961d5a99c8615be34c5aaa4b07 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 20:30:57 +0200 Subject: [PATCH 19/80] remove unimplemented auto rate menu entry --- pyfpdb/fpdb.pyw | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 094024e3..39e86592 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -653,7 +653,6 @@ class fpdb: - @@ -699,7 +698,6 @@ class fpdb: ('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), ('imapsummaries', None, '_Import Tourney Summaries through eMail/IMAP', 'I', 'Auto Import and HUD', self.import_imap_summaries), - ('autorate', None, 'Auto _Rating (todo)', 'R', 'Auto Rating (todo)', self.not_implemented), ('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), From ef98564d72717202584b2898da76db3ab2563bc0 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 20:32:49 +0200 Subject: [PATCH 20/80] remove unimplemented hand replayer and player details menu entries --- pyfpdb/fpdb.pyw | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 39e86592..cc167dfa 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -657,8 +657,6 @@ class fpdb: - - @@ -701,8 +699,6 @@ class fpdb: ('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), ('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), From 29d1120bf14bac1b86b6902842a58ba312925d79 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 20:43:01 +0200 Subject: [PATCH 21/80] removed unimplemented menu entry session replayer --- pyfpdb/fpdb.pyw | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index cc167dfa..730ca775 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -661,7 +661,6 @@ class fpdb: - @@ -703,7 +702,6 @@ class fpdb: ('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), From 4d17bd65e1eec3dfb3e9b6b87019b1af12553262 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 20:46:45 +0200 Subject: [PATCH 22/80] remove unimplemented menu entry create/del DB user --- pyfpdb/fpdb.pyw | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 730ca775..a785a325 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -322,11 +322,6 @@ 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") @@ -665,7 +660,6 @@ class fpdb: - @@ -705,7 +699,6 @@ class fpdb: ('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), From bc85c7fe8dad22f6c2c0feb3ba91999502594b7c Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 20:47:58 +0200 Subject: [PATCH 23/80] removed unimplemented menu entry abbreviations --- pyfpdb/fpdb.pyw | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index a785a325..3c9ce092 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -666,7 +666,6 @@ class fpdb: - @@ -704,7 +703,6 @@ class fpdb: ('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), ('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), @@ -857,9 +855,6 @@ class fpdb: self.lock.release() 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) From 00e156beba3cf218568c1009ca49a3106133dc15 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Fri, 9 Jul 2010 22:09:33 +0200 Subject: [PATCH 24/80] removed some unused stub methods --- pyfpdb/fpdb.pyw | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 3c9ce092..87910d98 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -325,19 +325,8 @@ class fpdb: def dia_database_stats(self, widget, data=None): self.warning_box("Unimplemented: 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_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, @@ -354,11 +343,6 @@ 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") From 4c00ef19cce6eaa1c75970a7b24d9c5ee32202df Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 02:07:47 +0200 Subject: [PATCH 25/80] implement simple DB stats - Hand and tourney count --- pyfpdb/Database.py | 12 ++++++++++++ pyfpdb/SQL.py | 3 +++ pyfpdb/fpdb.pyw | 5 +++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 2a12ed11..0c1cf85c 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -553,6 +553,18 @@ 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 get_actual_seat(self, hand_id, name): c = self.connection.cursor() c.execute(self.sql.query['get_actual_seat'], (hand_id, name)) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 9ad9f321..48fb9260 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -3834,6 +3834,9 @@ class Sql: %s )""" + self.query['getHandCount'] = "SELECT COUNT(id) FROM Hands" + self.query['getTourneyCount'] = "SELECT COUNT(id) FROM Tourneys" + if db_server == 'mysql': self.query['placeholder'] = u'%s' elif db_server == 'postgresql': diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 87910d98..f7ed8f24 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -323,7 +323,8 @@ class fpdb: + "other windows have been opened. Re-start fpdb to use this option.") 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()), diatitle="Database Statistics") + #end def dia_database_stats # def dia_get_db_root_credentials(self): # """obtains db root credentials from user""" @@ -685,7 +686,7 @@ class fpdb: ('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), + ('stats', None, '_Statistics', None, 'View Database Statistics', self.dia_database_stats), ('help', None, '_Help'), ('Logs', None, '_Log Messages', None, 'Log and Debug Messages', self.dia_logs), ('About', None, 'A_bout', None, 'About the program', self.dia_about), From 25893c96108426265a2414a88b0adf244983d870 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 02:09:58 +0200 Subject: [PATCH 26/80] GuiTourneyPlayerStats (mostly copied from GuiPlayerStats) works without data --- pyfpdb/Filters.py | 8 + pyfpdb/GuiPlayerStats.py | 5 +- pyfpdb/GuiTourneyPlayerStats.py | 167 +++++++++++++- pyfpdb/TourneyFilters.py | 384 ++++++++++++++++++++++++++++++-- pyfpdb/fpdb.pyw | 2 +- 5 files changed, 542 insertions(+), 24 deletions(-) diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 24981f65..2cb6bcd6 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -274,11 +274,13 @@ class Filters(threading.Thread): 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()]) ) @@ -314,6 +316,7 @@ class Filters(threading.Thread): _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: @@ -729,6 +732,7 @@ class Filters(threading.Thread): self.sbSeats['from'] = sb1 self.sbSeats['to'] = sb2 + #end def fillSeatsFrame def fillGroupsFrame(self, vbox, display): hbox = gtk.HBox(False, 0) @@ -833,6 +837,7 @@ class Filters(threading.Thread): 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(): @@ -846,6 +851,7 @@ class Filters(threading.Thread): 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) @@ -863,10 +869,12 @@ class Filters(threading.Thread): 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 diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 12bfefd1..fcee3075 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -164,6 +164,7 @@ class GuiPlayerStats (threading.Thread): 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() @@ -178,6 +179,7 @@ class GuiPlayerStats (threading.Thread): 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() @@ -213,6 +215,7 @@ class GuiPlayerStats (threading.Thread): 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() @@ -260,7 +263,7 @@ class GuiPlayerStats (threading.Thread): self.db.rollback() print "Stats page displayed in %4.2f seconds" % (time() - starttime) - #end def fillStatsFrame(self, vbox): + #end def createStatsTable def reset_style_render_func(self, treeviewcolumn, cell, model, iter): cell.set_property('foreground', 'black') diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 585b67f2..a8f2b840 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -16,10 +16,10 @@ #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 threading +import pygtk +pygtk.require('2.0') +import gtk #import os #import sys #from time import time, strftime @@ -27,10 +27,159 @@ #import Card #import fpdb_import #import Database -import TourneyFilters -import GuiPlayerStats #import Charset +import TourneyFilters -class GuiTourneyPlayerStats (GuiPlayerStats.GuiPlayerStats): - pass -#end class GuiTourneyPlayerStats \ No newline at end of file +class GuiTourneyPlayerStats (threading.Thread): + def __init__(self, config, db, sql, mainwin, debug=True): + self.conf = config + self.db = db + 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) + 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 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 fillStatsFrame(self, vbox): + sites = self.filters.getSites() + heroes = self.filters.getHeroes() + siteids = self.filters.getSiteIds() + seats = self.filters.getSeats() + 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 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 +#end class GuiTourneyPlayerStats diff --git a/pyfpdb/TourneyFilters.py b/pyfpdb/TourneyFilters.py index dcddba82..e536f0ea 100644 --- a/pyfpdb/TourneyFilters.py +++ b/pyfpdb/TourneyFilters.py @@ -15,28 +15,386 @@ #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 threading +import pygtk +pygtk.require('2.0') +import gtk +import gobject #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 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 +import Charset -class TourneyFilters(Filters.Filters): - pass -#end class TourneyFilters \ No newline at end of file +class TourneyFilters(threading.Thread): + 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:'} + + # 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 __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_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 __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 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) + + 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 get_vbox(self): + """returns the vbox of this thread""" + return self.mainVBox + #end def get_vbox + + def make_filter(self): + 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) + + # 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/fpdb.pyw b/pyfpdb/fpdb.pyw index f7ed8f24..94310870 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -861,7 +861,7 @@ class fpdb: 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.sql, self.window) + 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") From dc67197e23afe49f79a73e12b2595c4ee7536514 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 02:30:42 +0200 Subject: [PATCH 27/80] change starttime to startTime --- pyfpdb/GuiPlayerStats.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index fcee3075..586e55ea 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -218,7 +218,7 @@ class GuiPlayerStats (threading.Thread): #end def fillStatsFrame def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates, games): - starttime = time() + startTime = time() show_detail = True # Scrolled window for summary table @@ -262,7 +262,7 @@ class GuiPlayerStats (threading.Thread): ,sitenos, limits, type, seats, groups, dates, games) self.db.rollback() - print "Stats page displayed in %4.2f seconds" % (time() - starttime) + print "Stats page displayed in %4.2f seconds" % (time() - startTime) #end def createStatsTable def reset_style_render_func(self, treeviewcolumn, cell, model, iter): From b1e41a54ec95716c69b66edbf8b029c4e140a129 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 05:19:50 +0200 Subject: [PATCH 28/80] intermediate commit on the way to tourney stats --- pyfpdb/Database.py | 8 + pyfpdb/Filters.py | 12 +- pyfpdb/GuiPlayerStats.py | 5 +- pyfpdb/GuiTourneyPlayerStats.py | 303 ++++++++++++++++++++++++++++++-- pyfpdb/SQL.py | 9 + pyfpdb/TourneyFilters.py | 159 +++++++++++++++-- 6 files changed, 461 insertions(+), 35 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 0c1cf85c..e0f522b9 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -2042,6 +2042,14 @@ class Database: 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() + print "DB.getTourneyTypesIds result:",result + return result + #end def getTourneyTypesIds #end class Database # Class used to hold all the data needed to write a hand to the db diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 2cb6bcd6..d744c7bf 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -226,18 +226,22 @@ class Filters(threading.Thread): 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 = [] @@ -255,12 +259,14 @@ class Filters(threading.Thread): 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) @@ -323,7 +329,8 @@ class Filters(threading.Thread): self.numHands = int(w.get_text()) except: self.numHands = 0 -# log.debug("setting numHands:", self.numHands) + #log.debug("setting numHands:", self.numHands) + #end def __set_num_hands def createSiteLine(self, hbox, site): cb = gtk.CheckButton(site) @@ -354,6 +361,7 @@ class Filters(threading.Thread): #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() @@ -600,6 +608,7 @@ class Filters(threading.Thread): 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) @@ -899,6 +908,7 @@ class Filters(threading.Thread): 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 diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 586e55ea..13f898d2 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -442,8 +442,7 @@ class GuiPlayerStats (threading.Thread): sqlrow += 1 row += 1 vbox.show_all() - - #end def addGrid(self, query, vars, playerids, sitenos, limits, type, seats, groups, dates): + #end def addGrid def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates, games): having = '' @@ -609,7 +608,7 @@ class GuiPlayerStats (threading.Thread): #print "query =\n", query return(query) - #end def refineQuery(self, query, playerids, sitenos, limits): + #end def refineQuery def showDetailFilter(self, widget, data): detailDialog = gtk.Dialog(title="Detailed Filters", parent=self.main_window diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index a8f2b840..3a89c16d 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -22,12 +22,12 @@ pygtk.require('2.0') import gtk #import os #import sys -#from time import time, strftime +from time import time, strftime #import Card #import fpdb_import #import Database -#import Charset +import Charset import TourneyFilters class GuiTourneyPlayerStats (threading.Thread): @@ -41,7 +41,7 @@ class GuiTourneyPlayerStats (threading.Thread): self.liststore = [] # gtk.ListStore[] stores the contents of the grids self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids - filters_display = { "Heroes" : True, + filters_display = { "Heroes" : False, "Sites" : True, #"Games" : True, #"Limits" : True, @@ -67,6 +67,23 @@ class GuiTourneyPlayerStats (threading.Thread): #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 = [ ["tourneyTypeId", True, "TTypeId", 0.0, "%s", "str"] + , ["tourney", False, "Tourney", 0.0, "%s", "str"] # true not allowed for this line + #, ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code) + , ["tourneyCount", True, "#", 1.0, "%1.0f", "str"] + , ["1st", False, "1st", 1.0, "%3.1f", "str"] + , ["2nd", True, "2nd", 1.0, "%3.1f", "str"] + , ["3rd", True, "3rd", 1.0, "%3.1f", "str"] + , ["unknownRank", True, "unknown", 1.0, "%3.1f", "str"] + , ["itm", True, "ITM", 1.0, "%2.2f", "str"] + , ["roi", True, "ROI", 1.0, "%3.1f", "str"] + , ["profitPerTourney", True, "$/T", 1.0, "%3.1f", "str"]] + self.stats_frame = gtk.Frame() self.stats_frame.show() @@ -82,8 +99,119 @@ class GuiTourneyPlayerStats (threading.Thread): self.main_hbox.show() #end def __init__ - def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates, games): - starttime = time() + def addGrid(self, vbox, query, flags, tourneyTypes, playerids, sitenos, seats, dates): + 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, tourneyTypes, playerids, sitenos, seats, dates) + 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 createStatsTable(self, vbox, tourneyTypes, playerids, sitenos, seats, dates): + startTime = time() show_detail = True # Scrolled window for summary table @@ -97,9 +225,9 @@ class GuiTourneyPlayerStats (threading.Thread): # 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) + flags = [False, self.filters.getNumTourneys(), 0] + self.addGrid(swin, 'playerDetailedStats', flags, tourneyTypes, playerids + ,sitenos, seats, dates) if 'allplayers' in groups and groups['allplayers']: # can't currently do this combination so skip detailed table @@ -123,20 +251,20 @@ class GuiTourneyPlayerStats (threading.Thread): # Detailed table flags[0] = True flags[2] = 1 - self.addGrid(swin, 'playerDetailedStats', flags, playerids - ,sitenos, limits, type, seats, groups, dates, games) + self.addGrid(swin, 'playerDetailedStats', flags, playerids, sitenos, seats, dates) self.db.rollback() - print "Stats page displayed in %4.2f seconds" % (time() - starttime) + 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() - games = self.filters.getGames() sitenos = [] playerids = [] @@ -156,11 +284,8 @@ class GuiTourneyPlayerStats (threading.Thread): 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) + + self.createStatsTable(vbox, tourneyTypes, playerids, sitenos, seats, dates) #end def fillStatsFrame def get_vbox(self): @@ -168,6 +293,150 @@ class GuiTourneyPlayerStats (threading.Thread): return self.main_hbox #end def get_vbox + def refineQuery(self, query, flags, tourneyTypes, playerids, sitenos, seats, dates): + having = '' + holecards = flags[0] + numTourneys = flags[1] + + #if 'allplayers' in groups and groups['allplayers']: + nametest = "(hp.playerId)" + 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 numTourneys: + having = ' and count(1) > %d ' % (numTourneys,) + 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 refreshStats(self, widget, data): self.last_pos = self.stats_vbox.get_position() try: self.stats_vbox.destroy() diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 48fb9260..644ab0a3 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -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 ################################ @@ -3834,9 +3836,16 @@ class Sql: %s )""" + ################################ + # Counts for DB stats window + ################################ self.query['getHandCount'] = "SELECT COUNT(id) FROM Hands" + self.query['getTourneyCount'] = "SELECT COUNT(id) FROM Tourneys" + ################################ + # placeholders and substitution stuff + ################################ if db_server == 'mysql': self.query['placeholder'] = u'%s' elif db_server == 'postgresql': diff --git a/pyfpdb/TourneyFilters.py b/pyfpdb/TourneyFilters.py index e536f0ea..e1829830 100644 --- a/pyfpdb/TourneyFilters.py +++ b/pyfpdb/TourneyFilters.py @@ -23,7 +23,7 @@ import gobject #import os #import sys #from optparse import OptionParser -#from time import * +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: @@ -45,8 +45,14 @@ class TourneyFilters(threading.Thread): self.display = display self.filterText = {'playerstitle':'Hero:', 'sitestitle':'Sites:', 'seatstitle':'Number of Players:', - 'seatsbetween':'Between:', 'seatsand':'And:', 'datestitle':'Date:'} + '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) @@ -79,19 +85,44 @@ class TourneyFilters(threading.Thread): 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() @@ -105,6 +136,12 @@ class TourneyFilters(threading.Thread): 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() @@ -123,7 +160,7 @@ class TourneyFilters(threading.Thread): pname.set_text(player) pname.set_width_chars(20) hbox.pack_start(pname, False, True, 0) - pname.connect("changed", self.__set_hero_name, site) + #pname.connect("changed", self.__set_hero_name, site) # Added EntryCompletion but maybe comboBoxEntry is more flexible? (e.g. multiple choices) completion = gtk.EntryCompletion() @@ -137,7 +174,7 @@ class TourneyFilters(threading.Thread): _nt = (_n, ) liststore.append(_nt) - self.__set_hero_name(pname, site) + #self.__set_hero_name(pname, site) #end def createPlayerLine def createSiteLine(self, hbox, site): @@ -146,6 +183,13 @@ class TourneyFilters(threading.Thread): 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 @@ -216,7 +260,25 @@ class TourneyFilters(threading.Thread): 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 @@ -279,17 +341,77 @@ class TourneyFilters(threading.Thread): 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.sites = {} - self.seats = {} + self.tourneyTypes = {} + #self.tourneys = {} + self.sites = {} + self.seats = {} self.siteid = {} self.heroes = {} - self.boxes = {} + self.boxes = {} for site in self.conf.get_supported_sites(): #Get db site id for filtering later @@ -324,6 +446,15 @@ class TourneyFilters(threading.Thread): 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() From 97fbc7606cc2ba137dfb717cec9eb4788c626597 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 06:23:47 +0200 Subject: [PATCH 29/80] another intermediate commit for tourney stats --- pyfpdb/Filters.py | 2 +- pyfpdb/GuiTourneyPlayerStats.py | 123 +++++++++++++++----------------- pyfpdb/TourneyFilters.py | 10 ++- 3 files changed, 69 insertions(+), 66 deletions(-) diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index d744c7bf..a2420f7f 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -321,7 +321,7 @@ class Filters(threading.Thread): # 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])) + #log.debug("setting heroes[%s]: %s"%(site, self.heroes[site])) #end def __set_hero_name def __set_num_hands(self, w, val): diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 3a89c16d..c40e4875 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -34,6 +34,7 @@ class GuiTourneyPlayerStats (threading.Thread): 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 @@ -41,7 +42,7 @@ class GuiTourneyPlayerStats (threading.Thread): self.liststore = [] # gtk.ListStore[] stores the contents of the grids self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids - filters_display = { "Heroes" : False, + filters_display = { "Heroes" : True, "Sites" : True, #"Games" : True, #"Limits" : True, @@ -272,6 +273,7 @@ class GuiTourneyPlayerStats (threading.Thread): for site in sites: if sites[site] == True: sitenos.append(siteids[site]) + print "heroes",heroes _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: @@ -298,13 +300,16 @@ class GuiTourneyPlayerStats (threading.Thread): holecards = flags[0] numTourneys = flags[1] - #if 'allplayers' in groups and groups['allplayers']: - nametest = "(hp.playerId)" + 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 show player name column - [x for x in self.columns if x[0] == 'pname'][0][1] = True - if numTourneys: - having = ' and count(1) > %d ' % (numTourneys,) + # 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) @@ -355,54 +360,44 @@ class GuiTourneyPlayerStats (threading.Thread): 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 " + #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") + #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 @@ -414,7 +409,7 @@ class GuiTourneyPlayerStats (threading.Thread): query = query.replace("", flagtest) # allow for differences in sql cast() function: - if self.db.backend == self.MYSQL_INNODB: + if self.db.backend == self.db.MYSQL_INNODB: query = query.replace("", 'signed ') else: query = query.replace("", '') @@ -423,17 +418,17 @@ class GuiTourneyPlayerStats (threading.Thread): 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 + #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 + print "query at end of refine query:", query return(query) #end def refineQuery diff --git a/pyfpdb/TourneyFilters.py b/pyfpdb/TourneyFilters.py index e1829830..36039a4c 100644 --- a/pyfpdb/TourneyFilters.py +++ b/pyfpdb/TourneyFilters.py @@ -116,6 +116,14 @@ class TourneyFilters(threading.Thread): 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()) @@ -174,7 +182,7 @@ class TourneyFilters(threading.Thread): _nt = (_n, ) liststore.append(_nt) - #self.__set_hero_name(pname, site) + self.__set_hero_name(pname, site) #end def createPlayerLine def createSiteLine(self, hbox, site): From 4c1b0e3f96b609013a0dc5119b4460e4ae94e754 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 16:24:32 +0200 Subject: [PATCH 30/80] add extra enter to start screen --- pyfpdb/fpdb.pyw | 1 + 1 file changed, 1 insertion(+) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 94310870..b97d5ca0 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -883,6 +883,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. From ec0db8b7ccd026126c0a35e11abbd5a0859c3695 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 16:40:10 +0200 Subject: [PATCH 31/80] change version indicator to sth more appropriate --- pyfpdb/fpdb.pyw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index b97d5ca0..3ceb68b3 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -115,7 +115,7 @@ import Database import Configuration import Exceptions -VERSION = "0.21" +VERSION = "0.20 plus git" class fpdb: @@ -915,7 +915,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() From ca61189706c40fc97c79331f9540e43a4ab5f0b5 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 19:39:05 +0200 Subject: [PATCH 32/80] added TourneyTypeCount to DB Stats window --- pyfpdb/Database.py | 6 ++++++ pyfpdb/SQL.py | 2 +- pyfpdb/fpdb.pyw | 5 ++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index e0f522b9..14c998d1 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -565,6 +565,12 @@ class Database: 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)) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 644ab0a3..dba2928e 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -3840,8 +3840,8 @@ class Sql: # 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" ################################ # placeholders and substitution stuff diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 3ceb68b3..d734e418 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -323,7 +323,10 @@ class fpdb: + "other windows have been opened. Re-start fpdb to use this option.") def dia_database_stats(self, widget, data=None): - self.warning_box(str="Number of hands: "+str(self.db.getHandCount())+"\nNumber of tourneys: "+str(self.db.getTourneyCount()), diatitle="Database Statistics") + 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_get_db_root_credentials(self): From e9346f6b8228dfe469a702674f447ba53915f519 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 20:10:04 +0200 Subject: [PATCH 33/80] make it record source of lock holding, print if required this is mostly intended for devs --- pyfpdb/GuiBulkImport.py | 2 +- pyfpdb/fpdb.pyw | 10 ++++++---- pyfpdb/interlocks.py | 9 ++++++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pyfpdb/GuiBulkImport.py b/pyfpdb/GuiBulkImport.py index a25db1d2..83f841e2 100755 --- a/pyfpdb/GuiBulkImport.py +++ b/pyfpdb/GuiBulkImport.py @@ -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/fpdb.pyw b/pyfpdb/fpdb.pyw index d734e418..fc5e9fb6 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -814,12 +814,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() @@ -841,6 +842,7 @@ class fpdb: def release_global_lock(self): self.lock.release() + self.lockTakenBy=None print "Global lock released.\n" def tab_auto_import(self, widget, data=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(): From bf96e5103b26bb7c5aa28f52c3c6f989a32dfee8 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 20:39:43 +0200 Subject: [PATCH 34/80] ignore "Match Round" part of first line of HH --- pyfpdb/PokerStarsToFpdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 2beeb286..7cad0e97 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -92,7 +92,8 @@ 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)? + (-\sMatch.*) #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)? From b9762b0486776546d2027f04bb0928452c82a4ff Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 21:11:59 +0200 Subject: [PATCH 35/80] change remainder of fpdb.pyw to indicate source of lock --- pyfpdb/fpdb.pyw | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index fc5e9fb6..d97a0f75 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -300,7 +300,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, @@ -352,7 +352,7 @@ class fpdb: 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, @@ -375,7 +375,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, @@ -416,7 +416,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) @@ -468,7 +468,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 @@ -509,7 +509,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 @@ -595,8 +595,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)") From 4efd9dc2fb793dcf16d6ae380279d546d66348bb Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 21:29:07 +0200 Subject: [PATCH 36/80] fix import of normal tourneys and ring hands --- pyfpdb/PokerStarsToFpdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 7cad0e97..1e5d6916 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -92,7 +92,8 @@ 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 - (-\sMatch.*) #TODO: waiting for reply from user as to what this means + (-\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|)? From eb976203c452f421be9da83e36270fda99bb16be Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 22:24:32 +0200 Subject: [PATCH 37/80] intermediate commit for tourney stats --- pyfpdb/GuiTourneyPlayerStats.py | 83 ++++++++++++++++----------------- pyfpdb/SQL.py | 23 +++++++++ 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index c40e4875..211621d3 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -74,16 +74,17 @@ class GuiTourneyPlayerStats (threading.Thread): # columns to display, keys match column name returned by sql, values in tuple are: # is column displayed, column heading, xalignment, formatting, celltype self.columns = [ ["tourneyTypeId", True, "TTypeId", 0.0, "%s", "str"] - , ["tourney", False, "Tourney", 0.0, "%s", "str"] # true not allowed for this line - #, ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code) + #, ["tourney", False, "Tourney", 0.0, "%s", "str"] # true not allowed for this line + , ["playerName", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code) , ["tourneyCount", True, "#", 1.0, "%1.0f", "str"] , ["1st", False, "1st", 1.0, "%3.1f", "str"] , ["2nd", True, "2nd", 1.0, "%3.1f", "str"] , ["3rd", True, "3rd", 1.0, "%3.1f", "str"] , ["unknownRank", True, "unknown", 1.0, "%3.1f", "str"] - , ["itm", True, "ITM", 1.0, "%2.2f", "str"] - , ["roi", True, "ROI", 1.0, "%3.1f", "str"] - , ["profitPerTourney", True, "$/T", 1.0, "%3.1f", "str"]] + #, ["itm", True, "ITM", 1.0, "%2.2f", "str"] + #, ["roi", True, "ROI", 1.0, "%3.1f", "str"] + , ["invested", True, "Invested", 1.0, "%3.1f", "str"] + , ["profit", True, "Profit", 1.0, "%3.1f", "str"]] self.stats_frame = gtk.Frame() self.stats_frame.show() @@ -100,16 +101,15 @@ class GuiTourneyPlayerStats (threading.Thread): self.main_hbox.show() #end def __init__ - def addGrid(self, vbox, query, flags, tourneyTypes, playerids, sitenos, seats, dates): + def addGrid(self, vbox, query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates): + print "start of addGrid query", query 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, tourneyTypes, playerids, sitenos, seats, dates) - self.cursor.execute(tmp) + + query = self.sql.query[query] + query = self.refineQuery(query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates) + self.cursor.execute(query) result = self.cursor.fetchall() colnames = [desc[0].lower() for desc in self.cursor.description] @@ -221,13 +221,8 @@ class GuiTourneyPlayerStats (threading.Thread): 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.getNumTourneys(), 0] - self.addGrid(swin, 'playerDetailedStats', flags, tourneyTypes, playerids + numTourneys = self.filters.getNumTourneys() + self.addGrid(swin, 'tourneyPlayerDetailedStats', numTourneys, tourneyTypes, playerids ,sitenos, seats, dates) if 'allplayers' in groups and groups['allplayers']: @@ -295,41 +290,42 @@ class GuiTourneyPlayerStats (threading.Thread): return self.main_hbox #end def get_vbox - def refineQuery(self, query, flags, tourneyTypes, playerids, sitenos, seats, dates): + def refineQuery(self, query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates): having = '' - holecards = flags[0] - numTourneys = flags[1] - + + 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("", 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) + #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 = [] @@ -342,10 +338,11 @@ class GuiTourneyPlayerStats (threading.Thread): sitetest = sitetest.replace("L", "") sitetest = sitetest.replace(",)",")") sitetest = sitetest.replace("u'","'") - sitetest = "and gt.siteId in %s" % sitetest + sitetest = "and tt.siteId in %s" % sitetest#[1:-1] else: - sitetest = "and gt.siteId IS NULL" - query = query.replace("", sitetest) + 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'])) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index dba2928e..03d12424 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2257,6 +2257,29 @@ class Sql: ,s.name """ + if db_server == 'mysql': + self.query['tourneyPlayerDetailedStats'] = """ + select t.tourneyTypeId AS tourneyTypeId + ,p.name AS playerName + ,s.name AS siteName + ,sum(t.winnings)/100.0 AS profit + ,sum(t.buyin+t.fee)/100.0 AS invested + 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 = gt.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 + ,pname + ,s.name""" + elif db_server == 'postgresql': + self.query['tourneyPlayerDetailedStats'] = """TODO""" + elif db_server == 'sqlite': + self.query['tourneyPlayerDetailedStats'] = """TODO""" + if db_server == 'mysql': self.query['playerStats'] = """ SELECT From 043c3b5bca3874de66b5071b37ea5b2685deb6f5 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 23:08:05 +0200 Subject: [PATCH 38/80] fix import of PS FPP tourneys --- pyfpdb/PokerStarsToFpdb.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 1e5d6916..933f9444 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -256,16 +256,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] From 86a1f86b51472b0fe0c43e8f54b7fb6b7db1b693 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sat, 10 Jul 2010 23:21:36 +0200 Subject: [PATCH 39/80] tourney query returns key information - now just need to display it --- pyfpdb/GuiTourneyPlayerStats.py | 11 ++++++----- pyfpdb/SQL.py | 10 +++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 211621d3..4f548583 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -102,7 +102,7 @@ class GuiTourneyPlayerStats (threading.Thread): #end def __init__ def addGrid(self, vbox, query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates): - print "start of addGrid query", query + #print "start of addGrid query", query counter = 0 row = 0 sqlrow = 0 @@ -111,6 +111,7 @@ class GuiTourneyPlayerStats (threading.Thread): 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].lower() for desc in self.cursor.description] # pre-fetch some constant values: @@ -293,14 +294,14 @@ class GuiTourneyPlayerStats (threading.Thread): def refineQuery(self, query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates): having = '' - print "start of refinequery, playerids:",playerids + #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 + #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 @@ -341,7 +342,7 @@ class GuiTourneyPlayerStats (threading.Thread): 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 + #print "refinequery, sitetest before its use for replacement:",sitetest query = query.replace("", sitetest) if seats: @@ -425,7 +426,7 @@ class GuiTourneyPlayerStats (threading.Thread): # # 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 + #print "query at end of refine query:", query return(query) #end def refineQuery diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 03d12424..94a2b436 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2262,19 +2262,19 @@ class Sql: select t.tourneyTypeId AS tourneyTypeId ,p.name AS playerName ,s.name AS siteName - ,sum(t.winnings)/100.0 AS profit - ,sum(t.buyin+t.fee)/100.0 AS invested + ,sum(tp.winnings)/100.0 AS profit + ,sum(tt.buyin+tt.fee)/100.0 AS invested 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 = gt.siteId) + 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 - ,pname - ,s.name""" + ,playerName + ,siteName""" elif db_server == 'postgresql': self.query['tourneyPlayerDetailedStats'] = """TODO""" elif db_server == 'sqlite': From 604e2423490e006881341e3e9a2a7d0df8a4a086 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 00:37:25 +0200 Subject: [PATCH 40/80] handle FPP and added money tourneys, general currency improvements --- pyfpdb/PokerStarsSummary.py | 55 ++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index 6640daa9..dbda5781 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -20,6 +20,7 @@ from decimal import Decimal import datetime +from Exceptions import FpdbParseError import PokerStarsToFpdb from TourneySummary import * @@ -27,8 +28,11 @@ class PokerStarsSummary(TourneySummary): re_TourNo = re.compile("\#[0-9]+,") re_Entries = re.compile("[0-9]+") re_Prizepool = re.compile("\$[0-9]+\.[0-9]+") - re_Player = re.compile(u"""(?P[0-9]+):\s(?P.*)\s\(.*\),(\s\$(?P[0-9]+\.[0-9]+)\s\()?""") + re_Player = re.compile("""(?P[0-9]+):\s(?P.*)\s\(.*\),(\s\$(?P[0-9]+\.[0-9]+)\s\()?""") 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.compile("") @@ -37,35 +41,60 @@ class PokerStarsSummary(TourneySummary): self.tourNo = self.re_TourNo.findall(lines[0])[0][1:-1] #ignore game and limit type as thats not recorded - if lines[1].find("$")!=-1: + 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") + raise FpdbParseError("didn't recognise buyin currency in:"+lines[1]) - result=self.re_BuyInFee.search(lines[1]) - result=result.groupdict() - self.buyin=int(100*Decimal(result['BUYIN'])) - self.fee=int(100*Decimal(result['FEE'])) + 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 - self.entries = self.re_Entries.findall(lines[2])[0] + 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] - self.prizepool = self.re_Prizepool.findall(lines[3])[0] + result=self.re_Added.search(lines[currentLine]) + if result: + print "detected addon [currentLine]", lines[currentLine] + 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 + + self.prizepool = self.re_Prizepool.findall(lines[currentLine])[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[4]) + 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 = PokerStarsToFpdb.removeET(self.startTime) + currentLine+=1 - result=self.re_DateTime.search(lines[5]) + 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.endTime= datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" + currentLine+=1 - for i in range(6,len(lines)-2): #lines with rank and winnings info + 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]) @@ -78,6 +107,6 @@ class PokerStarsSummary(TourneySummary): else: winnings=0 - self.addPlayer(rank, name, winnings, "USD", None, None, None)#TODO: currency, ko/addon/rebuy count -> need examples! + self.addPlayer(rank, name, winnings, self.currency, None, None, None)#TODO: currency, ko/addon/rebuy count -> need examples! #end def parseSummary #end class PokerStarsSummary From 6a3587e92e5183fbbc10d319bf2bf86c0d96f555 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 00:37:32 +0200 Subject: [PATCH 41/80] failed attempt at making SQLite accept non-ASCII player names for this --- pyfpdb/Database.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 14c998d1..659b375c 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -822,9 +822,10 @@ class Database: def get_player_id(self, config, siteName, playerName): c = self.connection.cursor() + 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, siteName)) + c.execute(self.sql.query['get_player_id'], (playerNameUtf, siteNameUtf)) row = c.fetchone() if row: return row[0] From ae2c32f90283f31d7037a8ccb330551ebe6c0e5e Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 01:39:52 +0200 Subject: [PATCH 42/80] imports still running tournaments, except for TourneysPlayers --- pyfpdb/PokerStarsSummary.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index dbda5781..ba727bd7 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -40,6 +40,7 @@ class PokerStarsSummary(TourneySummary): lines=self.summaryText.splitlines() self.tourNo = self.re_TourNo.findall(lines[0])[0][1:-1] #ignore game and limit type as thats not recorded + print "tourNo:",self.tourNo 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" @@ -65,20 +66,23 @@ class PokerStarsSummary(TourneySummary): 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] + print "after entries lines[currentLine]", lines[currentLine] result=self.re_Added.search(lines[currentLine]) if result: - print "detected addon [currentLine]", lines[currentLine] 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] - self.prizepool = self.re_Prizepool.findall(lines[currentLine])[0] - self.prizepool = self.prizepool[1:-3]+self.prizepool[-2:] - currentLine+=1 + result=self.re_Prizepool.findall(lines[currentLine]) + if result: + print "prizepool result", 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]) @@ -89,10 +93,12 @@ class PokerStarsSummary(TourneySummary): currentLine+=1 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.endTime= datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" - currentLine+=1 + if result: + print "endtime result", 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 for i in range(currentLine,len(lines)-2): #lines with rank and winnings info if lines[i].find(":")==-1: From 87fbd56091b845307ca87beb8b1b3e6e80516248 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 01:55:15 +0200 Subject: [PATCH 43/80] imports still playing tourney's TP, but not winnings of finishers --- pyfpdb/Database.py | 11 ++++++++--- pyfpdb/PokerStarsSummary.py | 19 +++++++++++++------ pyfpdb/TourneySummary.py | 11 ++++++++--- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 659b375c..c4c13dbe 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -2043,9 +2043,14 @@ class Database: (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] - 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])) + 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 diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index ba727bd7..a71baa40 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -28,7 +28,7 @@ class PokerStarsSummary(TourneySummary): 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_Player = re.compile("""(?P[0-9]+):\s(?P.*)\s\(.*\),(\s\$(?P[0-9]+\.[0-9]+)\s\()?(\s(?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 @@ -40,7 +40,7 @@ class PokerStarsSummary(TourneySummary): lines=self.summaryText.splitlines() self.tourNo = self.re_TourNo.findall(lines[0])[0][1:-1] #ignore game and limit type as thats not recorded - print "tourNo:",self.tourNo + #print "tourNo:",self.tourNo 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" @@ -66,7 +66,7 @@ class PokerStarsSummary(TourneySummary): 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] + #print "after entries lines[currentLine]", lines[currentLine] result=self.re_Added.search(lines[currentLine]) if result: @@ -75,11 +75,10 @@ class PokerStarsSummary(TourneySummary): self.addedCurrency=result['CURRENCY'] #print "TODO: implement added:",self.added,self.addedCurrency currentLine+=1 - print "after added/entries lines[currentLine]", lines[currentLine] + #print "after added/entries lines[currentLine]", lines[currentLine] result=self.re_Prizepool.findall(lines[currentLine]) if result: - print "prizepool result", result self.prizepool = result[0] self.prizepool = self.prizepool[1:-3]+self.prizepool[-2:] currentLine+=1 @@ -94,12 +93,14 @@ class PokerStarsSummary(TourneySummary): result=self.re_DateTime.search(lines[currentLine]) if result: - print "endtime result", 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 @@ -108,11 +109,17 @@ class PokerStarsSummary(TourneySummary): rank=result['RANK'] name=result['NAME'] winnings=result['WINNINGS'] + if winnings: winnings=int(100*Decimal(winnings)) else: winnings=0 + 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/TourneySummary.py b/pyfpdb/TourneySummary.py index f7632ea3..c57a9f0d 100644 --- a/pyfpdb/TourneySummary.py +++ b/pyfpdb/TourneySummary.py @@ -230,9 +230,14 @@ 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: From 7925747213eb97b33156f2c44d63ed4c31f548de Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 02:07:03 +0200 Subject: [PATCH 44/80] now records winnings for finishers in unfinished tourneys --- pyfpdb/PokerStarsSummary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index a71baa40..e8744fbf 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -28,7 +28,7 @@ class PokerStarsSummary(TourneySummary): 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\()?(\s(?Pstill\splaying))?""") + re_Player = re.compile("""(?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 From c58ade20b32831c94be41a58bdbd801857dadcb1 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 02:27:48 +0200 Subject: [PATCH 45/80] fix PS summary player/winnings/rank recording --- pyfpdb/PokerStarsSummary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index e8744fbf..e652a95c 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -28,7 +28,7 @@ class PokerStarsSummary(TourneySummary): 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]+))?(?Pstill\splaying)?""") + re_Player = re.compile("""(?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 From d3c57ee9fcac5aab1c8e2e53bfbd0a752df2c4b5 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 03:13:30 +0200 Subject: [PATCH 46/80] repair playerstats --- pyfpdb/fpdb.pyw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index d97a0f75..d5dc0656 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -103,7 +103,7 @@ import GuiLogView import GuiDatabase import GuiBulkImport import ImapSummaries -import GuiRingPlayerStats +import GuiPlayerStats import GuiTourneyPlayerStats import GuiPositionalStats import GuiTableViewer @@ -860,7 +860,7 @@ class fpdb: self.add_and_display_tab(bulk_tab, "Bulk Import") def tab_ring_player_stats(self, widget, data=None): - new_ps_thread = GuiRingPlayerStats.GuiRingPlayerStats(self.config, self.sql, self.window) + new_ps_thread = GuiPlayerStats.GuiPlayerStats(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, "Ring Player Stats") From 8e9d5883220664035f9927ab01b2e23c85eeb073 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 03:13:58 +0200 Subject: [PATCH 47/80] first version of Tourney Player Stats works --- pyfpdb/Database.py | 1 - pyfpdb/GuiPlayerStats.py | 2 + pyfpdb/GuiTourneyPlayerStats.py | 75 +++++++++++++++++---------------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index c4c13dbe..7546edd9 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -2059,7 +2059,6 @@ class Database: c = self.connection.cursor() c.execute(self.sql.query['getTourneyTypesIds']) result = c.fetchall() - print "DB.getTourneyTypesIds result:",result return result #end def getTourneyTypesIds #end class Database diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 13f898d2..80786d76 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -267,6 +267,7 @@ class GuiPlayerStats (threading.Thread): 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') @@ -332,6 +333,7 @@ class GuiPlayerStats (threading.Thread): 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 diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 4f548583..68d28934 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -30,6 +30,8 @@ from time import time, strftime import Charset import TourneyFilters +colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5 + class GuiTourneyPlayerStats (threading.Thread): def __init__(self, config, db, sql, mainwin, debug=True): self.conf = config @@ -103,21 +105,24 @@ class GuiTourneyPlayerStats (threading.Thread): 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].lower() for desc in self.cursor.description] + 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]] - hgametypeid_idx = colnames.index('hgametypeid') - + #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]) @@ -157,7 +162,7 @@ class GuiTourneyPlayerStats (threading.Thread): 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)) + 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) @@ -173,40 +178,14 @@ class GuiTourneyPlayerStats (threading.Thread): 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 + 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) - #print treerow sqlrow += 1 row += 1 vbox.show_all() @@ -226,9 +205,9 @@ class GuiTourneyPlayerStats (threading.Thread): self.addGrid(swin, 'tourneyPlayerDetailedStats', numTourneys, tourneyTypes, playerids ,sitenos, seats, dates) - if 'allplayers' in groups and groups['allplayers']: + #if 'allplayers' in groups and groups['allplayers']: # can't currently do this combination so skip detailed table - show_detail = False + show_detail = False if show_detail: # Separator @@ -269,7 +248,6 @@ class GuiTourneyPlayerStats (threading.Thread): for site in sites: if sites[site] == True: sitenos.append(siteids[site]) - print "heroes",heroes _hname = Charset.to_utf8(heroes[site]) result = self.db.get_player_id(self.conf, site, _hname) if result is not None: @@ -444,4 +422,29 @@ class GuiTourneyPlayerStats (threading.Thread): 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 From 8232085be90019752eee7ded6a1a413f57e5d9a1 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 03:42:48 +0200 Subject: [PATCH 48/80] display 1st/2nd/3rd in tourney player stats --- pyfpdb/SQL.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 94a2b436..8b608f89 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2259,11 +2259,15 @@ class Sql: if db_server == 'mysql': self.query['tourneyPlayerDetailedStats'] = """ - select t.tourneyTypeId AS tourneyTypeId + select s.name AS siteName + ,t.tourneyTypeId AS tourneyTypeId ,p.name AS playerName - ,s.name AS siteName - ,sum(tp.winnings)/100.0 AS profit - ,sum(tt.buyin+tt.fee)/100.0 AS invested + ,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(CASE WHEN rank = NULL THEN 1 ELSE 0 END) AS unknownRank + ,SUM(tp.winnings)/100.0 AS profit + ,SUM(tt.buyin+tt.fee)/100.0 AS invested from TourneysPlayers tp inner join Tourneys t on (t.id = tp.tourneyId) inner join TourneyTypes tt on (tt.Id = t.tourneyTypeId) From 7ddb032205945676394da214f7fca82a0177f255 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 04:37:48 +0200 Subject: [PATCH 49/80] display unknownRank in tourney player stats --- pyfpdb/SQL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 8b608f89..7e7209dd 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2265,7 +2265,7 @@ class Sql: ,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(CASE WHEN rank = NULL THEN 1 ELSE 0 END) AS unknownRank + ,SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END) AS unknownRank ,SUM(tp.winnings)/100.0 AS profit ,SUM(tt.buyin+tt.fee)/100.0 AS invested from TourneysPlayers tp From 9815ac976bdaa02317845e646b14bcdca672b01b Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 04:47:15 +0200 Subject: [PATCH 50/80] display buyin and sitename rather than TT.id --- pyfpdb/GuiTourneyPlayerStats.py | 3 ++- pyfpdb/SQL.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 68d28934..6d7540a5 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -75,8 +75,9 @@ class GuiTourneyPlayerStats (threading.Thread): # 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 = [ ["tourneyTypeId", True, "TTypeId", 0.0, "%s", "str"] + self.columns = [ ["siteName", True, "Site", 0.0, "%s", "str"] #, ["tourney", False, "Tourney", 0.0, "%s", "str"] # true not allowed for this line + , ["buyIn", True, "BuyIn", 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"] , ["1st", False, "1st", 1.0, "%3.1f", "str"] diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 7e7209dd..21255e49 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2261,6 +2261,7 @@ class Sql: self.query['tourneyPlayerDetailedStats'] = """ select s.name AS siteName ,t.tourneyTypeId AS tourneyTypeId + ,(CASE WHEN tt.currency = "USD" THEN tt.buyIn/100.0 ELSE tt.buyIn END) AS buyIn ,p.name AS playerName ,SUM(CASE WHEN rank = 1 THEN 1 ELSE 0 END) AS 1st ,SUM(CASE WHEN rank = 2 THEN 1 ELSE 0 END) AS 2nd From 913fa7d7d81b080d3ead0a5ea69afae3799b8474 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 04:59:31 +0200 Subject: [PATCH 51/80] fix formatting and add tourneycount to TPS --- pyfpdb/GuiTourneyPlayerStats.py | 12 ++++++------ pyfpdb/SQL.py | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 6d7540a5..90a7ecff 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -80,14 +80,14 @@ class GuiTourneyPlayerStats (threading.Thread): , ["buyIn", True, "BuyIn", 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"] - , ["1st", False, "1st", 1.0, "%3.1f", "str"] - , ["2nd", True, "2nd", 1.0, "%3.1f", "str"] - , ["3rd", True, "3rd", 1.0, "%3.1f", "str"] - , ["unknownRank", True, "unknown", 1.0, "%3.1f", "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, "unknown", 1.0, "%1.0f", "str"] #, ["itm", True, "ITM", 1.0, "%2.2f", "str"] #, ["roi", True, "ROI", 1.0, "%3.1f", "str"] - , ["invested", True, "Invested", 1.0, "%3.1f", "str"] - , ["profit", True, "Profit", 1.0, "%3.1f", "str"]] + , ["invested", True, "Invested", 1.0, "%3.2f", "str"] + , ["profit", True, "Profit", 1.0, "%3.2f", "str"]] self.stats_frame = gtk.Frame() self.stats_frame.show() diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 21255e49..a8784544 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2263,6 +2263,7 @@ class Sql: ,t.tourneyTypeId AS tourneyTypeId ,(CASE WHEN tt.currency = "USD" THEN tt.buyIn/100.0 ELSE tt.buyIn END) AS buyIn ,p.name AS playerName + ,COUNT(1) AS tourneyCount ,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 From 33760fae9c94421004b02097e443e42e3467eb09 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 05:11:43 +0200 Subject: [PATCH 52/80] TPS displays ITM% --- pyfpdb/GuiTourneyPlayerStats.py | 2 +- pyfpdb/SQL.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 90a7ecff..ab654b79 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -80,11 +80,11 @@ class GuiTourneyPlayerStats (threading.Thread): , ["buyIn", True, "BuyIn", 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, "unknown", 1.0, "%1.0f", "str"] - #, ["itm", True, "ITM", 1.0, "%2.2f", "str"] #, ["roi", True, "ROI", 1.0, "%3.1f", "str"] , ["invested", True, "Invested", 1.0, "%3.2f", "str"] , ["profit", True, "Profit", 1.0, "%3.2f", "str"]] diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index a8784544..2d27ee45 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2264,10 +2264,11 @@ class Sql: ,(CASE WHEN tt.currency = "USD" THEN tt.buyIn/100.0 ELSE tt.buyIn END) AS buyIn ,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(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END) AS unknownRank ,SUM(tp.winnings)/100.0 AS profit ,SUM(tt.buyin+tt.fee)/100.0 AS invested from TourneysPlayers tp From ed096b68723c13b6d2f90012ad115a8a6bc9c3fb Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 05:22:36 +0200 Subject: [PATCH 53/80] TPS displays ROI% --- pyfpdb/GuiTourneyPlayerStats.py | 8 ++++---- pyfpdb/SQL.py | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index ab654b79..105ed880 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -85,10 +85,10 @@ class GuiTourneyPlayerStats (threading.Thread): , ["2nd", True, "2nd", 1.0, "%1.0f", "str"] , ["3rd", True, "3rd", 1.0, "%1.0f", "str"] , ["unknownRank", True, "unknown", 1.0, "%1.0f", "str"] - #, ["roi", True, "ROI", 1.0, "%3.1f", "str"] - , ["invested", True, "Invested", 1.0, "%3.2f", "str"] - , ["profit", True, "Profit", 1.0, "%3.2f", "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"]] + self.stats_frame = gtk.Frame() self.stats_frame.show() diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 2d27ee45..9f9747b8 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2269,8 +2269,9 @@ class Sql: ,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 profit - ,SUM(tt.buyin+tt.fee)/100.0 AS invested + ,SUM(tp.winnings)/100.0 AS won + ,SUM(tt.buyin+tt.fee)/100.0 AS spent + ,SUM(tp.winnings)/SUM(tt.buyin+tt.fee)*100.0-100 AS roi from TourneysPlayers tp inner join Tourneys t on (t.id = tp.tourneyId) inner join TourneyTypes tt on (tt.Id = t.tourneyTypeId) From 7bf746370584b9f04d45f93f93965fa0eb073fc2 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 05:28:43 +0200 Subject: [PATCH 54/80] TPS displays currency and fee now --- pyfpdb/GuiTourneyPlayerStats.py | 26 ++++++++++++++------------ pyfpdb/SQL.py | 2 ++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 105ed880..e3ce2691 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -75,19 +75,21 @@ class GuiTourneyPlayerStats (threading.Thread): # 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 - , ["buyIn", True, "BuyIn", 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"] + self.columns = [ ["siteName", True, "Site", 0.0, "%s", "str"] + #,["tourney", False, "Tourney", 0.0, "%s", "str"] # true not allowed for this line + , ["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, "unknown", 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"]] + , ["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"]] self.stats_frame = gtk.Frame() self.stats_frame.show() diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 9f9747b8..0588f2de 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2261,7 +2261,9 @@ class Sql: 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 ,p.name AS playerName ,COUNT(1) AS tourneyCount ,SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END) AS unknownRank From 6038342de736d9e4f93462aaf10c4ec3a99a2537 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 05:35:41 +0200 Subject: [PATCH 55/80] TPS displays profit/tourney --- pyfpdb/GuiTourneyPlayerStats.py | 3 ++- pyfpdb/SQL.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index e3ce2691..2d901c94 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -89,7 +89,8 @@ class GuiTourneyPlayerStats (threading.Thread): , ["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"]] + , ["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() diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 0588f2de..77a3883c 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2273,7 +2273,8 @@ class Sql: ,SUM(CASE WHEN rank = 3 THEN 1 ELSE 0 END) AS 3rd ,SUM(tp.winnings)/100.0 AS won ,SUM(tt.buyin+tt.fee)/100.0 AS spent - ,SUM(tp.winnings)/SUM(tt.buyin+tt.fee)*100.0-100 AS roi + ,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) From e526501384e90d2a8528ee76841d3236d7bb9b12 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 05:49:58 +0200 Subject: [PATCH 56/80] rename GuiPlayerStats to GuiRingPlayerStats and Filters to RingFilters --- pyfpdb/Filters.py | 948 ----------------------------------- pyfpdb/GuiGraphViewer.py | 4 +- pyfpdb/GuiPlayerStats.py | 680 ------------------------- pyfpdb/GuiPositionalStats.py | 4 +- pyfpdb/GuiRingPlayerStats.py | 678 ++++++++++++++++++++++++- pyfpdb/GuiSessionViewer.py | 4 +- pyfpdb/RingFilters.py | 946 +++++++++++++++++++++++++++++++++- pyfpdb/SQL.py | 4 +- pyfpdb/fpdb.pyw | 4 +- 9 files changed, 1597 insertions(+), 1675 deletions(-) delete mode 100644 pyfpdb/Filters.py delete mode 100644 pyfpdb/GuiPlayerStats.py diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py deleted file mode 100644 index a2420f7f..00000000 --- a/pyfpdb/Filters.py +++ /dev/null @@ -1,948 +0,0 @@ -#!/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 * -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 - #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['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 - #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/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index 96026864..68cbacc7 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -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/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py deleted file mode 100644 index 80786d76..00000000 --- a/pyfpdb/GuiPlayerStats.py +++ /dev/null @@ -1,680 +0,0 @@ -#!/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 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 - #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/GuiPositionalStats.py b/pyfpdb/GuiPositionalStats.py index 724d0f84..1764c5ea 100644 --- a/pyfpdb/GuiPositionalStats.py +++ b/pyfpdb/GuiPositionalStats.py @@ -24,7 +24,7 @@ from time import time, strftime import fpdb_import import Database -import Filters +import RingFilters import FpdbSQLQueries class GuiPositionalStats (threading.Thread): @@ -58,7 +58,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/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index 11e8a1b1..68878606 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -#Copyright 2010 Steffen Schaumburg +#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. @@ -15,22 +15,666 @@ #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 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 Card +import fpdb_import +import Database import RingFilters -import GuiPlayerStats -#import Charset +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 GuiRingPlayerStats (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 = 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() + + + + -class GuiRingPlayerStats (GuiPlayerStats.GuiPlayerStats): - pass -#end class GuiRingPlayerStats \ No newline at end of file diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index 05249051..38cd7b74 100755 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -45,7 +45,7 @@ except ImportError, inst: import Card import fpdb_import import Database -import Filters +import RingFilters import FpdbSQLQueries import Charset @@ -96,7 +96,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/RingFilters.py b/pyfpdb/RingFilters.py index 4780b131..44fc790a 100644 --- a/pyfpdb/RingFilters.py +++ b/pyfpdb/RingFilters.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -#Copyright 2010 Steffen Schaumburg +#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. @@ -15,28 +15,934 @@ #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 * -#import gobject +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 +import logging # logging has been set up in fpdb.py or HUD_main.py, use their settings: -#log = logging.getLogger("filter") +log = logging.getLogger("filter") -#import Configuration -#import Database -#import SQL -#import Charset -import Filters +import Configuration +import Database +import SQL +import Charset -class RingFilters(Filters.Filters): - pass -#end class RingFilters \ No newline at end of file + +class RingFilters(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 + #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['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 + #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 77a3883c..cc2e9cc1 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1941,12 +1941,12 @@ 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""" diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index d5dc0656..d97a0f75 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -103,7 +103,7 @@ import GuiLogView import GuiDatabase import GuiBulkImport import ImapSummaries -import GuiPlayerStats +import GuiRingPlayerStats import GuiTourneyPlayerStats import GuiPositionalStats import GuiTableViewer @@ -860,7 +860,7 @@ class fpdb: self.add_and_display_tab(bulk_tab, "Bulk Import") def tab_ring_player_stats(self, widget, data=None): - new_ps_thread = GuiPlayerStats.GuiPlayerStats(self.config, self.sql, self.window) + 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, "Ring Player Stats") From be671859e75a9c22e99619a8b9ae255d11e4ab87 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 05:58:00 +0200 Subject: [PATCH 57/80] create superclasses for Gui*PlayerStats and *Filters --- pyfpdb/Filters.py | 22 ++++++++++++++++++++++ pyfpdb/GuiPlayerStats.py | 22 ++++++++++++++++++++++ pyfpdb/GuiRingPlayerStats.py | 3 ++- pyfpdb/GuiTourneyPlayerStats.py | 3 ++- pyfpdb/RingFilters.py | 4 ++-- pyfpdb/TourneyFilters.py | 3 ++- 6 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 pyfpdb/Filters.py create mode 100644 pyfpdb/GuiPlayerStats.py diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py new file mode 100644 index 00000000..b211b698 --- /dev/null +++ b/pyfpdb/Filters.py @@ -0,0 +1,22 @@ +#!/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 + +class Filters(threading.Thread): + pass +#end class Filters diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py new file mode 100644 index 00000000..34e8e122 --- /dev/null +++ b/pyfpdb/GuiPlayerStats.py @@ -0,0 +1,22 @@ +#!/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 + +class GuiPlayerStats(threading.Thread): + pass +#end class GuiPlayerStats diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index 68878606..29aaf44c 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -29,11 +29,12 @@ 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 (threading.Thread): +class GuiRingPlayerStats (GuiPlayerStats.GuiPlayerStats): def __init__(self, config, querylist, mainwin, debug=True): self.debug = debug diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 2d901c94..2ce1e2bb 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -29,10 +29,11 @@ from time import time, strftime #import Database import Charset import TourneyFilters +import GuiPlayerStats colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5 -class GuiTourneyPlayerStats (threading.Thread): +class GuiTourneyPlayerStats (GuiPlayerStats.GuiPlayerStats): def __init__(self, config, db, sql, mainwin, debug=True): self.conf = config self.db = db diff --git a/pyfpdb/RingFilters.py b/pyfpdb/RingFilters.py index 44fc790a..8c8eae2f 100644 --- a/pyfpdb/RingFilters.py +++ b/pyfpdb/RingFilters.py @@ -35,9 +35,9 @@ import Configuration import Database import SQL import Charset +import Filters - -class RingFilters(threading.Thread): +class RingFilters(Filters.Filters): def __init__(self, db, config, qdict, display = {}, debug=True): # config and qdict are now redundant self.debug = debug diff --git a/pyfpdb/TourneyFilters.py b/pyfpdb/TourneyFilters.py index 36039a4c..977c94ec 100644 --- a/pyfpdb/TourneyFilters.py +++ b/pyfpdb/TourneyFilters.py @@ -34,8 +34,9 @@ log = logging.getLogger("filter") #import Database #import SQL import Charset +import Filters -class TourneyFilters(threading.Thread): +class TourneyFilters(Filters.Filters): def __init__(self, db, config, qdict, display = {}, debug=True): self.debug = debug self.db = db From 5ae650e8b79284f1e80182a07abdebfe396d8835 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 06:01:44 +0200 Subject: [PATCH 58/80] reduce imports in RingFilters --- pyfpdb/RingFilters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/RingFilters.py b/pyfpdb/RingFilters.py index 8c8eae2f..be4aba43 100644 --- a/pyfpdb/RingFilters.py +++ b/pyfpdb/RingFilters.py @@ -22,7 +22,7 @@ import gtk import os import sys from optparse import OptionParser -from time import * +from time import gmtime, mktime, strftime, strptime import gobject #import pokereval From 17b3b770227399a17cf6733cb2d9b1d7e67a59be Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 06:08:57 +0200 Subject: [PATCH 59/80] TPS: correct spent display for PSFP games --- pyfpdb/SQL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index cc2e9cc1..a78fca5f 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2272,7 +2272,7 @@ class Sql: ,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(tt.buyin+tt.fee)/100.0 AS spent + ,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 From cfb0a3f3d54b73f6d94e8fffd77b63c813ba3f51 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 06:37:25 +0200 Subject: [PATCH 60/80] remove default value and allow null for Hands.rush --- pyfpdb/Database.py | 2 +- pyfpdb/SQL.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 7546edd9..a0dd1495 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 127 +DB_VERSION = 128 # Variance created as sqlite has a bunch of undefined aggregate functions. diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index a78fca5f..77e2bd4a 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -259,7 +259,7 @@ class Sql: 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, @@ -296,7 +296,7 @@ class Sql: 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, @@ -332,7 +332,7 @@ class Sql: 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, From 822c592db82297467cdcd4b0caf02e6cef12c6f9 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 06:42:37 +0200 Subject: [PATCH 61/80] DB: add category and limitType to TourneyTypes --- pyfpdb/Database.py | 2 +- pyfpdb/SQL.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index a0dd1495..5e591d1b 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 128 +DB_VERSION = 129 # Variance created as sqlite has a bunch of undefined aggregate functions. diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 77e2bd4a..415fe3af 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -370,6 +370,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, @@ -395,6 +397,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, @@ -419,6 +423,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, From d7397739086b8725293b65ebc6d689dd09ce4f26 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 07:39:19 +0200 Subject: [PATCH 62/80] GUI: display config file location in about dialogue --- pyfpdb/fpdb.pyw | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index d97a0f75..1fa23528 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -235,6 +235,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)") @@ -261,11 +262,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: ") From 5fee5136a9cc43fc0eb8c917ebf0bf10f87a242b Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 08:05:54 +0200 Subject: [PATCH 63/80] DB: renamed Hands.handStart to Hands.startTime for consistency --- pyfpdb/AlchemyMappings.py | 4 +-- pyfpdb/AlchemyTables.py | 2 +- pyfpdb/Database.py | 8 ++--- pyfpdb/DerivedStats.py | 2 +- pyfpdb/SQL.py | 70 +++++++++++++++++++-------------------- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/pyfpdb/AlchemyMappings.py b/pyfpdb/AlchemyMappings.py index f4884de3..15cdf70d 100644 --- a/pyfpdb/AlchemyMappings.py +++ b/pyfpdb/AlchemyMappings.py @@ -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 61feb0d5..84c4b9f0 100644 --- a/pyfpdb/AlchemyTables.py +++ b/pyfpdb/AlchemyTables.py @@ -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/Database.py b/pyfpdb/Database.py index 5e591d1b..71034563 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 129 +DB_VERSION = 130 # Variance created as sqlite has a bunch of undefined aggregate functions. @@ -1423,9 +1423,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']) @@ -1541,7 +1541,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'], diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index bae557ad..96928486 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -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/SQL.py b/pyfpdb/SQL.py index 415fe3af..47c9740f 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -255,7 +255,7 @@ 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, @@ -292,7 +292,7 @@ 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, @@ -328,7 +328,7 @@ 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, @@ -1620,7 +1620,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 */ @@ -1723,7 +1723,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 */ @@ -1826,7 +1826,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 */ @@ -1896,23 +1896,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 @@ -1924,7 +1924,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 @@ -1936,7 +1936,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 @@ -2043,7 +2043,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 @@ -2140,7 +2140,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 @@ -2238,7 +2238,7 @@ class Sql: and h.seats - and datetime(h.handStart) + and datetime(h.startTime) group by hgameTypeId ,hp.playerId ,gt.base @@ -2396,7 +2396,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 @@ -2499,7 +2499,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 @@ -2634,7 +2634,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 @@ -2771,7 +2771,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 @@ -2792,13 +2792,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""" + GROUP BY h.startTime, hp.handId, hp.sawShowdown, hp.totalProfit + ORDER BY h.startTime""" #################################### @@ -2806,38 +2806,38 @@ 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 date_format(h.startTime, '%Y-%m-%d') AND hp.tourneysPlayersId IS NULL 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 h.startTime AND hp.tourneysPlayersId IS NULL 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 h.startTime AND hp.tourneysPlayersId IS NULL ORDER by time""" @@ -2946,7 +2946,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) @@ -3025,7 +3025,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'] = """ @@ -3125,7 +3125,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) @@ -3204,7 +3204,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'] = """ @@ -3304,7 +3304,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) @@ -3383,7 +3383,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'] = """ From 9283d7b5792417110eb48b31e50af142d7a6edb8 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 09:47:05 +0200 Subject: [PATCH 64/80] some fixes for handStart rename, fixes for TT.category/limitType, dumpDatabase method and menu entry --- pyfpdb/Database.py | 54 +++++++++++++++++++++++++++++--------- pyfpdb/Hand.py | 2 +- pyfpdb/PokerStarsToFpdb.py | 2 +- pyfpdb/SQL.py | 14 +++++++--- pyfpdb/TourneySummary.py | 4 +-- pyfpdb/fpdb.pyw | 11 ++++++-- 6 files changed, 65 insertions(+), 22 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 71034563..f739c7db 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -290,6 +290,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 @@ -1375,17 +1403,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): @@ -1976,11 +2004,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() @@ -1989,7 +2017,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) ) @@ -2112,7 +2140,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/Hand.py b/pyfpdb/Hand.py index 59311ae5..49553f85 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -1580,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/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 933f9444..d7386e23 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -238,7 +238,7 @@ 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?? diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 47c9740f..532cdad3 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -3646,6 +3646,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 @@ -3655,9 +3657,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 @@ -3734,7 +3736,7 @@ class Sql: gametypeid, sitehandno, tourneyId, - handstart, + startTime, importtime, seats, maxseats, @@ -3883,6 +3885,12 @@ class Sql: 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'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 ################################ diff --git a/pyfpdb/TourneySummary.py b/pyfpdb/TourneySummary.py index c57a9f0d..75d4bcde 100644 --- a/pyfpdb/TourneySummary.py +++ b/pyfpdb/TourneySummary.py @@ -334,9 +334,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/fpdb.pyw b/pyfpdb/fpdb.pyw index 1fa23528..763d3ef1 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -336,6 +336,11 @@ class fpdb: diatitle="Database Statistics") #end def dia_database_stats + def dia_dump_db(self, widget, data=None): + self.db.dumpDatabase("database-dump.sql") + #end def dia_database_stats + + # def dia_get_db_root_credentials(self): # """obtains db root credentials from user""" # user, pw=None, None @@ -658,7 +663,8 @@ class fpdb: - + + @@ -696,7 +702,8 @@ class fpdb: ('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', 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'), ('Logs', None, '_Log Messages', None, 'Log and Debug Messages', self.dia_logs), ('About', None, 'A_bout', None, 'About the program', self.dia_about), From d53b3e1b4ace6adcaefc87369e16f0252acd1678 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 12:47:28 +0200 Subject: [PATCH 65/80] DB: add backings table, fix a couple of log entries --- pyfpdb/Database.py | 13 +++++++++---- pyfpdb/SQL.py | 27 ++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index f739c7db..513c4ee9 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 130 +DB_VERSION = 131 # 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} ] ] @@ -1135,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") @@ -1212,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) @@ -1220,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) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 532cdad3..955422b3 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -130,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 ################################ From 79282270fb234cab6de27f5e61d825f67694a94d Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 14:33:27 +0200 Subject: [PATCH 66/80] DUMP: fix dumpDB Gametypes, add Backings --- pyfpdb/SQL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 955422b3..950726e1 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -3913,7 +3913,7 @@ class Sql: ################################ # queries for dumpDatabase ################################ - for table in (u'Autorates', u'GameTypes', u'Hands', u'HandsActions', u'HandsPlayers', u'HudCache', u'Players', u'Settings', u'Sites', u'TourneyTypes', u'Tourneys', u'TourneysPlayers'): + 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 ################################ From 519c49904aa056aecbac4d6e6dec50fd06e18c07 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Sun, 11 Jul 2010 14:49:19 +0200 Subject: [PATCH 67/80] CLEANUP: remove FpdbSQLQueries as its been migrated elsewhere --- pyfpdb/FpdbSQLQueries.py | 77 ------------------------------------ pyfpdb/GuiPositionalStats.py | 1 - pyfpdb/GuiSessionViewer.py | 1 - 3 files changed, 79 deletions(-) delete mode 100644 pyfpdb/FpdbSQLQueries.py diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py deleted file mode 100644 index cb131f51..00000000 --- a/pyfpdb/FpdbSQLQueries.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/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. - -############################################################################ -# -# 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/GuiPositionalStats.py b/pyfpdb/GuiPositionalStats.py index 1764c5ea..b114e892 100644 --- a/pyfpdb/GuiPositionalStats.py +++ b/pyfpdb/GuiPositionalStats.py @@ -25,7 +25,6 @@ from time import time, strftime import fpdb_import import Database import RingFilters -import FpdbSQLQueries class GuiPositionalStats (threading.Thread): def __init__(self, config, querylist, debug=True): diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index 38cd7b74..f8c0d4cc 100755 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -46,7 +46,6 @@ import Card import fpdb_import import Database import RingFilters -import FpdbSQLQueries import Charset class GuiSessionViewer (threading.Thread): From 78d7d1b941a4961b0c72f53f63f42c132dee9603 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Mon, 12 Jul 2010 08:13:09 +0200 Subject: [PATCH 68/80] comment out duplicate query --- pyfpdb/SQL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 950726e1..0ad51905 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1980,7 +1980,7 @@ class Sql: # 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""" From 8d5e3399737e7c09d0e2e08f8bb8e4af9c7ed225 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Mon, 12 Jul 2010 08:17:36 +0200 Subject: [PATCH 69/80] make Filters work inspite of new tt.limitType --- pyfpdb/SQL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 0ad51905..a1eec221 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1985,7 +1985,7 @@ class Sql: from Gametypes ORDER by type, limitType DESC, bigBlind DESC""" self.query['getLimits3'] = """select DISTINCT type - , limitType + , gt.limitType , case type when 'ring' then bigBlind else buyin From 627c044b6fdcd0679de005de17adfda698056112 Mon Sep 17 00:00:00 2001 From: Worros Date: Mon, 12 Jul 2010 18:19:01 +1000 Subject: [PATCH 70/80] Fix grapher so that only cash limits show up --- pyfpdb/RingFilters.py | 2 +- pyfpdb/SQL.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pyfpdb/RingFilters.py b/pyfpdb/RingFilters.py index be4aba43..db7ed4de 100644 --- a/pyfpdb/RingFilters.py +++ b/pyfpdb/RingFilters.py @@ -624,7 +624,7 @@ class RingFilters(Filters.Filters): vbox.pack_start(vbox1, False, False, 0) self.boxes['limits'] = vbox1 - self.cursor.execute(self.sql.query['getLimits3']) + 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} diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index a1eec221..26d5dc6b 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1986,13 +1986,18 @@ class Sql: ORDER by type, limitType DESC, bigBlind DESC""" self.query['getLimits3'] = """select DISTINCT type , gt.limitType - , case type + , 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, 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': self.query['playerDetailedStats'] = """ From 32e1cb7b7f06eed5bc7ad9991695bde322f1887b Mon Sep 17 00:00:00 2001 From: steffen123 Date: Mon, 12 Jul 2010 10:20:53 +0200 Subject: [PATCH 71/80] another fix for the introduction of TT.limitType --- pyfpdb/SQL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index a1eec221..6228ab24 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1992,7 +1992,7 @@ class Sql: end as bb_or_buyin from Gametypes gt cross join TourneyTypes tt - order by type, limitType DESC, bb_or_buyin DESC""" + order by type, gt.limitType DESC, bb_or_buyin DESC""" if db_server == 'mysql': self.query['playerDetailedStats'] = """ From 585fd2feb2652d40895bdf67e9fa9d8e217b7450 Mon Sep 17 00:00:00 2001 From: Worros Date: Mon, 12 Jul 2010 18:41:28 +1000 Subject: [PATCH 72/80] Update graph and session queries to identify ring games --- pyfpdb/SQL.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 26d5dc6b..e2a72bf5 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2826,7 +2826,7 @@ class Sql: AND h.startTime < '' - AND hp.tourneysPlayersId IS NULL + AND gt.type is 'ring' GROUP BY h.startTime, hp.handId, hp.sawShowdown, hp.totalProfit ORDER BY h.startTime""" @@ -2844,7 +2844,7 @@ class Sql: INNER JOIN Players p on (p.Id = hp.playerId) WHERE hp.playerId in AND date_format(h.startTime, '%Y-%m-%d') - AND hp.tourneysPlayersId IS NULL + AND gt.type is 'ring' ORDER by time""" elif db_server == 'postgresql': self.query['sessionStats'] = """ @@ -2856,7 +2856,7 @@ class Sql: INNER JOIN Players p on (p.Id = hp.playerId) WHERE hp.playerId in AND h.startTime - AND hp.tourneysPlayersId IS NULL + AND gt.type is 'ring' ORDER by time""" elif db_server == 'sqlite': self.query['sessionStats'] = """ @@ -2868,7 +2868,7 @@ class Sql: INNER JOIN Players p on (p.Id = hp.playerId) WHERE hp.playerId in AND h.startTime - AND hp.tourneysPlayersId IS NULL + AND gt.type is 'ring' ORDER by time""" From 04ec51d7c16ab76842a74333d2e47524a9392e97 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Mon, 12 Jul 2010 11:00:50 +0200 Subject: [PATCH 73/80] DB: Fix capitalisation of Gametypes table in SQLite --- pyfpdb/Database.py | 2 +- pyfpdb/SQL.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 513c4ee9..bf0ed73b 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 131 +DB_VERSION = 132 # Variance created as sqlite has a bunch of undefined aggregate functions. diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 6228ab24..818a2d0e 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -190,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, From d208d627a472e8815a93870d72bf1e718e1eaa3b Mon Sep 17 00:00:00 2001 From: steffen123 Date: Mon, 12 Jul 2010 16:48:12 +0200 Subject: [PATCH 74/80] stop sqlite&postgres from doing except _mysql.... sqlite gives another error on quit unfortunately --- pyfpdb/fpdb.pyw | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 763d3ef1..4ba3bb4a 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -844,11 +844,16 @@ 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.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() - except _mysql_exceptions.OperationalError: # oh, damn, we're already disconnected - pass self.statusIcon.set_visible(False) self.window.destroy() # explicitly destroy to allow child windows to close cleanly From 1d571cabee94434aad96059b2e7f26d82e2f83ce Mon Sep 17 00:00:00 2001 From: steffen123 Date: Mon, 12 Jul 2010 18:04:30 +0200 Subject: [PATCH 75/80] windows: by sqlcoder, fixes importing files with greek characters --- pyfpdb/fpdb_import.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index e8455c01..ac569c95 100755 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -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: From d4e54e6aaee424055a4c0e39c87b57711147e0ca Mon Sep 17 00:00:00 2001 From: steffen123 Date: Tue, 13 Jul 2010 18:04:26 +0200 Subject: [PATCH 76/80] POLISH: it can now quit when it wasn't able to connect to DB --- pyfpdb/fpdb.pyw | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 4ba3bb4a..1afec904 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -845,15 +845,18 @@ class fpdb: print "Quitting normally" # TODO: check if current settings differ from profile, if so offer to save or abort - if self.db.backend==self.db.MYSQL_INNODB: - try: + 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() - 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() + pass self.statusIcon.set_visible(False) self.window.destroy() # explicitly destroy to allow child windows to close cleanly From 8db0445d7104d0805bc42fbfe8e6f54c36108229 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Tue, 13 Jul 2010 18:04:53 +0200 Subject: [PATCH 77/80] IMAP: close server connection when done --- pyfpdb/ImapSummaries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/ImapSummaries.py b/pyfpdb/ImapSummaries.py index 4f8813dd..f2874725 100755 --- a/pyfpdb/ImapSummaries.py +++ b/pyfpdb/ImapSummaries.py @@ -74,8 +74,8 @@ def run(config, db): print "completed running Imap import, closing server connection" #finally: # try: - # server.close() + server.close() # finally: # pass - #server.logout() + server.logout() \ No newline at end of file From 8ebea1a5ce7d205500bf48912db9a4ad7fb75ad1 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Tue, 13 Jul 2010 20:17:15 +0200 Subject: [PATCH 78/80] PSS: now records game category and limit type Not for mixed games - again, I need examples --- pyfpdb/PokerStarsSummary.py | 24 +++++++++++++++++++++--- pyfpdb/TourneySummary.py | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index e652a95c..99e4633d 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -25,22 +25,40 @@ import PokerStarsToFpdb from TourneySummary import * class PokerStarsSummary(TourneySummary): + 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]+))?(?Pstill\splaying)?""") + 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.compile("") + 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 - #print "tourNo:",self.tourNo + + 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] 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" diff --git a/pyfpdb/TourneySummary.py b/pyfpdb/TourneySummary.py index 75d4bcde..89d7388f 100644 --- a/pyfpdb/TourneySummary.py +++ b/pyfpdb/TourneySummary.py @@ -92,6 +92,7 @@ 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 = {} From b8968591c595d7751b54710fdb089deffb2eaa0c Mon Sep 17 00:00:00 2001 From: steffen123 Date: Tue, 13 Jul 2010 20:21:05 +0200 Subject: [PATCH 79/80] TPS: display limitType and category --- pyfpdb/GuiTourneyPlayerStats.py | 2 ++ pyfpdb/SQL.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 2ce1e2bb..7523f594 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -78,6 +78,8 @@ class GuiTourneyPlayerStats (GuiPlayerStats.GuiPlayerStats): # 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"] diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index e6a39f2f..96a45d2c 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2300,6 +2300,8 @@ class Sql: ,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 From 522a745eadc47c30756016f8789ad2d929190b59 Mon Sep 17 00:00:00 2001 From: steffen123 Date: Tue, 13 Jul 2010 20:37:06 +0200 Subject: [PATCH 80/80] REFACTOR: move timezone changing method to HHC so it can used by other parsers --- pyfpdb/HandHistoryConverter.py | 17 +++++++++++++++++ pyfpdb/PokerStarsSummary.py | 3 ++- pyfpdb/PokerStarsToFpdb.py | 17 +---------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index acabbd8b..4554c2e8 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -494,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/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index 99e4633d..73844e57 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -21,6 +21,7 @@ from decimal import Decimal import datetime from Exceptions import FpdbParseError +from HandHistoryConverter import * import PokerStarsToFpdb from TourneySummary import * @@ -106,7 +107,7 @@ class PokerStarsSummary(TourneySummary): 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 = PokerStarsToFpdb.removeET(self.startTime) + self.startTime = HandHistoryConverter.changeTimezone(self.startTime, "ET", "UTC") currentLine+=1 result=self.re_DateTime.search(lines[currentLine]) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index d7386e23..0a9e47fc 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -26,21 +26,6 @@ from decimal import Decimal # PokerStars HH Format -def removeET(time): - # 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 def removeET - class PokerStars(HandHistoryConverter): # Class Variables @@ -244,7 +229,7 @@ class PokerStars(HandHistoryConverter): #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" - hand.startTime = removeET(hand.startTime) + hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC") if key == 'HID': hand.handid = info[key] if key == 'TOURNO':