diff --git a/packaging/gentoo/current_stable.ebuild b/packaging/gentoo/current_stable.ebuild index 7a27feb0..7986c092 100644 --- a/packaging/gentoo/current_stable.ebuild +++ b/packaging/gentoo/current_stable.ebuild @@ -49,7 +49,7 @@ src_install() { make_desktop_entry ${PN} prepgamesdirs - fperms +x "${GAMES_DATADIR}"/${PN}/pyfpdb/*.pyw + chmod +x ${D}/"${GAMES_DATADIR}"/${PN}/pyfpdb/*.pyw } pkg_postinst() { diff --git a/packaging/gentoo/current_testing.ebuild b/packaging/gentoo/current_testing.ebuild index 05483b62..b5583fa7 100644 --- a/packaging/gentoo/current_testing.ebuild +++ b/packaging/gentoo/current_testing.ebuild @@ -49,7 +49,7 @@ src_install() { make_desktop_entry ${PN} prepgamesdirs - fperms +x "${GAMES_DATADIR}"/${PN}/pyfpdb/*.pyw + chmod +x ${D}/"${GAMES_DATADIR}"/${PN}/pyfpdb/*.pyw } pkg_postinst() { diff --git a/packaging/gentoo/fpdb-9999.ebuild b/packaging/gentoo/fpdb-9999.ebuild new file mode 100644 index 00000000..39b88b76 --- /dev/null +++ b/packaging/gentoo/fpdb-9999.ebuild @@ -0,0 +1,65 @@ +# Copyright 1999-2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# created by Steffen Schaumburg, steffen@schaumburger.info + +inherit eutils +inherit games +inherit git + +EAPI="2" +NEED_PYTHON=2.5 + +DESCRIPTION="A free/open source tracker/HUD for use with online poker" +HOMEPAGE="http://fpdb.wiki.sourceforge.net/" +EGIT_REPO_URI="git://git.assembla.com/fpdb.git" + +LICENSE="AGPL-3" +SLOT="0" +KEYWORDS="~amd64 ~x86" +#note: this should work on other architectures too, please send me your experiences + +IUSE="graph 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 + graph? ( dev-python/numpy + dev-python/matplotlib[gtk] ) + dev-python/python-xlib + dev-python/pytz" +DEPEND="${RDEPEND}" + +src_unpack() { + git_src_unpack +} + +src_install() { + insinto "${GAMES_DATADIR}"/${PN} + doins -r gfx + doins -r pyfpdb + doins readme.txt + + exeinto "${GAMES_DATADIR}"/${PN} + doexe run_fpdb.py + + dodir "${GAMES_BINDIR}" + dosym "${GAMES_DATADIR}"/${PN}/run_fpdb.py "${GAMES_BINDIR}"/${PN} + + newicon gfx/fpdb-icon.png ${PN}.png + make_desktop_entry ${PN} + + prepgamesdirs + chmod +x ${D}/"${GAMES_DATADIR}"/${PN}/pyfpdb/*.pyw +} + +pkg_postinst() { + games_pkg_postinst + 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." + elog "You can find the instructions on the project's website." +} diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 19f8371f..618d27da 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -452,8 +452,8 @@ class Email: self.fetchType = node.getAttribute("fetchType") 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) + return " siteName=%s\n fetchType=%s\n host = %s\n username = %s\n password = %s\n useSsl = %s\n folder = %s\n" \ + % (self.siteName, self.fetchType, self.host, self.username, self.password, self.useSsl, self.folder) class HudUI: def __init__(self, node): @@ -534,6 +534,39 @@ class GUICashStats(list): self.append( [col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment] ) + def get_defaults(self): + """A list of defaults to be called, should there be no entry in config""" + defaults = [ [u'game', u'Game', True, True, u'%s', u'str', 0.0], + [u'hand', u'Hand', False, False, u'%s', u'str', 0.0], + [u'plposition', u'Posn', False, False, u'%s', u'str', 1.0], + [u'pname', u'Name', False, False, u'%s', u'str', 0.0], + [u'n', u'Hds', True, True, u'%1.0f', u'str', 1.0], + [u'avgseats', u'Seats', False, False, u'%3.1f', u'str', 1.0], + [u'vpip', u'VPIP', True, True, u'%3.1f', u'str', 1.0], + [u'pfr', u'PFR', True, True, u'%3.1f', u'str', 1.0], + [u'pf3', u'PF3', True, True, u'%3.1f', u'str', 1.0], + [u'aggfac', u'AggFac', True, True, u'%2.2f', u'str', 1.0], + [u'aggfrq', u'AggFreq', True, True, u'%3.1f', u'str', 1.0], + [u'conbet', u'ContBet', True, True, u'%3.1f', u'str', 1.0], + [u'rfi', u'RFI', True, True, u'%3.1f', u'str', 1.0], + [u'steals', u'Steals', True, True, u'%3.1f', u'str', 1.0], + [u'saw_f', u'Saw_F', True, True, u'%3.1f', u'str', 1.0], + [u'sawsd', u'SawSD', True, True, u'%3.1f', u'str', 1.0], + [u'wtsdwsf', u'WtSDwsF', True, True, u'%3.1f', u'str', 1.0], + [u'wmsd', u'W$SD', True, True, u'%3.1f', u'str', 1.0], + [u'flafq', u'FlAFq', True, True, u'%3.1f', u'str', 1.0], + [u'tuafq', u'TuAFq', True, True, u'%3.1f', u'str', 1.0], + [u'rvafq', u'RvAFq', True, True, u'%3.1f', u'str', 1.0], + [u'pofafq', u'PoFAFq', False, False, u'%3.1f', u'str', 1.0], + [u'net', u'Net($)', True, True, u'%6.2f', u'cash', 1.0], + [u'bbper100', u'bb/100', True, True, u'%4.2f', u'str', 1.0], + [u'rake', u'Rake($)', True, True, u'%6.2f', u'cash', 1.0], + [u'bb100xr', u'bbxr/100', True, True, u'%4.2f', u'str', 1.0], + [u'variance', u'Variance', True, True, u'%5.2f', u'str', 1.0] + ] + for col in defaults: + self.append (col) + # def __str__(self): # s = "" # for l in self: @@ -595,11 +628,14 @@ class Config: self.db_selected = None # database the user would like to use self.tv = None self.general = General() + self.emails = {} self.gui_cash_stats = GUICashStats() for gen_node in doc.getElementsByTagName("general"): self.general.add_elements(node=gen_node) # add/overwrite elements in self.general + if doc.getElementsByTagName("gui_cash_stats") == []: + self.gui_cash_stats.get_defaults() for gcs_node in doc.getElementsByTagName("gui_cash_stats"): self.gui_cash_stats.add_elements(node=gcs_node) # add/overwrite elements in self.gui_cash_stats @@ -655,7 +691,8 @@ class Config: for email_node in doc.getElementsByTagName("email"): email = Email(node = email_node) - self.email = email + if email.siteName!="": #FIXME: Why on earth is this needed? + self.emails[email.siteName+"_"+email.fetchType]=email for hui_node in doc.getElementsByTagName('hud_ui'): hui = HudUI(node = hui_node) @@ -702,6 +739,12 @@ class Config: if site_node.getAttribute("site_name") == site: return site_node + def getEmailNode(self, siteName, fetchType): + for emailNode in self.doc.getElementsByTagName("email"): + if emailNode.getAttribute("siteName") == siteName and emailNode.getAttribute("fetchType") == fetchType: + return emailNode + #end def getEmailNode + def getGameNode(self,gameName): """returns DOM game node for a given game""" for gameNode in self.doc.getElementsByTagName("game"): @@ -776,6 +819,15 @@ class Config: else: return(l) + def editEmail(self, siteName, fetchType, newEmail): + emailNode = self.getEmailNode(siteName, fetchType) + emailNode.setAttribute("host", newEmail.host) + emailNode.setAttribute("username", newEmail.username) + emailNode.setAttribute("password", newEmail.password) + emailNode.setAttribute("folder", newEmail.folder) + emailNode.setAttribute("useSsl", newEmail.useSsl) + #end def editEmail + def edit_layout(self, site_name, max, width = None, height = None, fav_seat = None, locations = None): site_node = self.get_site_node(site_name) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index cf62dcad..ed4fa0a9 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 139 +DB_VERSION = 142 # Variance created as sqlite has a bunch of undefined aggregate functions. @@ -140,7 +140,7 @@ 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':'tourneysPlayersId', 'drop':0} , {'tab':'Backings', 'col':'playerId', 'drop':0} ] , [ # indexes for sqlite (list index 4) @@ -155,7 +155,7 @@ 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':'tourneysPlayersId', 'drop':0} , {'tab':'Backings', 'col':'playerId', 'drop':0} ] ] @@ -265,7 +265,7 @@ class Database: #ISOLATION_LEVEL_SERIALIZABLE = 2 - if self.backend == self.SQLITE and self.database == ':memory:' and self.wrongDbVersion: + if self.backend == self.SQLITE and self.database == ':memory:' and self.wrongDbVersion and self.is_connected(): log.info("sqlite/:memory: - creating") self.recreate_tables() self.wrongDbVersion = False @@ -289,7 +289,8 @@ class Database: self.saveActions = False if self.import_options['saveActions'] == False else True - self.connection.rollback() # make sure any locks taken so far are released + if self.is_connected(): + self.connection.rollback() # make sure any locks taken so far are released #end def __init__ def dumpDatabase(self): @@ -342,7 +343,6 @@ class Database: self.db_server = db_params['db-server'] self.database = db_params['db-databaseName'] self.host = db_params['db-host'] - self.__connected = True def connect(self, backend=None, host=None, database=None, user=None, password=None, create=False): @@ -363,6 +363,7 @@ class Database: MySQLdb = pool.manage(MySQLdb, pool_size=5) try: self.connection = MySQLdb.connect(host=host, user=user, passwd=password, db=database, use_unicode=True) + self.__connected = True #TODO: Add port option except MySQLdb.Error, ex: if ex.args[0] == 1045: @@ -384,20 +385,21 @@ class Database: # flat out wrong # sqlcoder: This database only connect failed in my windows setup?? # Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere? - connected = False + self.__connected = False if self.host == "localhost" or self.host == "127.0.0.1": try: self.connection = psycopg2.connect(database = database) - connected = True + self.__connected = True except: # direct connection failed so try user/pass/... version pass - if not connected: + if not self.is_connected(): try: self.connection = psycopg2.connect(host = host, user = user, password = password, database = database) + self.__connected = True except Exception, ex: if 'Connection refused' in ex.args[0]: # meaning eg. db not running @@ -426,6 +428,7 @@ class Database: log.info("Connecting to SQLite: %(database)s" % {'database':self.db_path}) if os.path.exists(database) or create: self.connection = sqlite3.connect(self.db_path, detect_types=sqlite3.PARSE_DECLTYPES ) + self.__connected = True sqlite3.register_converter("bool", lambda x: bool(int(x))) sqlite3.register_adapter(bool, lambda x: "1" if x else "0") self.connection.create_function("floor", 1, math.floor) @@ -443,9 +446,10 @@ class Database: else: raise FpdbError("unrecognised database backend:"+str(backend)) - self.cursor = self.connection.cursor() - self.cursor.execute(self.sql.query['set tx level']) - self.check_version(database=database, create=create) + if self.is_connected(): + self.cursor = self.connection.cursor() + self.cursor.execute(self.sql.query['set tx level']) + self.check_version(database=database, create=create) def check_version(self, database, create): @@ -499,6 +503,10 @@ class Database: self.connection.rollback() def connected(self): + """ now deprecated, use is_connected() instead """ + return self.__connected + + def is_connected(self): return self.__connected def get_cursor(self): @@ -1442,17 +1450,41 @@ class Database: h_start = self.hero_hudstart_def if v_start is None: v_start = self.villain_hudstart_def + if self.hero_ids == {}: - where = "" + where = "WHERE hp.tourneysPlayersId IS NULL" else: - where = "where ( hp.playerId not in " + str(tuple(self.hero_ids.values())) \ + where = "where ((( hp.playerId not in " + str(tuple(self.hero_ids.values())) \ + " and h.startTime > '" + v_start + "')" \ + " or ( hp.playerId in " + str(tuple(self.hero_ids.values())) \ - + " and h.startTime > '" + h_start + "')" - rebuild_sql = self.sql.query['rebuildHudCache'].replace('', where) - + + " and h.startTime > '" + h_start + "'))" \ + + " AND hp.tourneysPlayersId IS NULL)" + rebuild_sql_cash = self.sql.query['rebuildHudCache'].replace('', "") + rebuild_sql_cash = rebuild_sql_cash.replace('', "") + rebuild_sql_cash = rebuild_sql_cash.replace('', "") + rebuild_sql_cash = rebuild_sql_cash.replace('', "") + rebuild_sql_cash = rebuild_sql_cash.replace('', where) + #print "rebuild_sql_cash:",rebuild_sql_cash self.get_cursor().execute(self.sql.query['clearHudCache']) - self.get_cursor().execute(rebuild_sql) + self.get_cursor().execute(rebuild_sql_cash) + + if self.hero_ids == {}: + where = "WHERE hp.tourneysPlayersId >= 0" + else: + where = "where ((( hp.playerId not in " + str(tuple(self.hero_ids.values())) \ + + " and h.startTime > '" + v_start + "')" \ + + " or ( hp.playerId in " + str(tuple(self.hero_ids.values())) \ + + " and h.startTime > '" + h_start + "'))" \ + + " AND hp.tourneysPlayersId >= 0)" + rebuild_sql_tourney = self.sql.query['rebuildHudCache'].replace('', ",tourneyTypeId") + rebuild_sql_tourney = rebuild_sql_tourney.replace('', ",t.tourneyTypeId") + rebuild_sql_tourney = rebuild_sql_tourney.replace('', """INNER JOIN TourneysPlayers tp ON (tp.id = hp.tourneysPlayersId) + INNER JOIN Tourneys t ON (t.id = tp.tourneyId)""") + rebuild_sql_tourney = rebuild_sql_tourney.replace('', ",t.tourneyTypeId") + rebuild_sql_tourney = rebuild_sql_tourney.replace('', where) + #print "rebuild_sql_tourney:",rebuild_sql_tourney + + self.get_cursor().execute(rebuild_sql_tourney) self.commit() print "Rebuild hudcache took %.1f seconds" % (time() - stime,) except: @@ -2021,8 +2053,12 @@ class Database: result=cursor.fetchone() if result != None: - expectedValues = ('comment', 'tourneyName', 'matrixIdProcessed', 'totalRebuyCount', 'totalAddOnCount', - 'prizepool', 'startTime', 'entries', 'commentTs', 'endTime') + if self.backend == Database.PGSQL: + expectedValues = ('comment', 'tourneyname', 'matrixIdProcessed', 'totalRebuyCount', 'totalAddOnCount', + 'prizepool', 'startTime', 'entries', 'commentTs', 'endTime') + else: + expectedValues = ('comment', 'tourneyName', 'matrixIdProcessed', 'totalRebuyCount', 'totalAddOnCount', + 'prizepool', 'startTime', 'entries', 'commentTs', 'endTime') updateDb=False resultDict = dict(zip(columnNames, result)) @@ -2115,6 +2151,32 @@ class Database: result = c.fetchall() return result #end def getTourneyTypesIds + + def getTourneyInfo(self, siteName, tourneyNo): + c = self.get_cursor() + c.execute(self.sql.query['getTourneyInfo'], (siteName, tourneyNo)) + columnNames=c.description + + names=[] + for column in columnNames: + names.append(column[0]) + + data=c.fetchone() + return (names,data) + #end def getTourneyInfo + + def getTourneyPlayerInfo(self, siteName, tourneyNo, playerName): + c = self.get_cursor() + c.execute(self.sql.query['getTourneyPlayerInfo'], (siteName, tourneyNo, playerName)) + columnNames=c.description + + names=[] + for column in columnNames: + names.append(column[0]) + + data=c.fetchone() + return (names,data) + #end def getTourneyPlayerInfo #end class Database # Class used to hold all the data needed to write a hand to the db diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index 26e95394..938e7b3b 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -290,9 +290,11 @@ class DerivedStats(): # print "p_actions:", self.pfba(actions), "p_folds:", self.pfba(actions, l=('folds',)), "alliners:", alliners # pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners) - # hand.players includes people that are sitting out on some sites. - # Those that posted an ante should have been deal cards. - p_in = set([x[0] for x in hand.actions['BLINDSANTES']] + [x[0] for x in hand.actions['PREFLOP']]) + # hand.players includes people that are sitting out on some sites for cash games + # actionStreets[1] is 'DEAL', 'THIRD', 'PREFLOP', so any player dealt cards + # must act on this street if dealt cards. Almost certainly broken for the 'all-in blind' case + # and right now i don't care - CG + p_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]]) for (i, street) in enumerate(hand.actionStreets): actions = hand.actions[street] diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index cfeb7de1..179824ac 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -31,18 +31,23 @@ class Fulltilt(HandHistoryConverter): codepage = ["utf-16", "cp1252", "utf-8"] siteId = 1 # Needs to match id entry in Sites database + substitutions = { + 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes + 'LS' : "\$|\xe2\x82\xac|" # legal currency symbols - Euro(cp1252, utf-8) + } + # Static regexes re_GameInfo = re.compile('''.*\#(?P[0-9]+):\s (?:(?P.+)\s\((?P\d+)\),\s)? .+ - -\s(?P\$|)? + -\s(?P[%(LS)s]|)? (?P[.0-9]+)/ - \$?(?P[.0-9]+)\s + [%(LS)s]?(?P[.0-9]+)\s (Ante\s\$?(?P[.0-9]+)\s)?-\s - \$?(?P[.0-9]+\sCap\s)? + [%(LS)s]?(?P[.0-9]+\sCap\s)? (?P(No\sLimit|Pot\sLimit|Limit))?\s (?P(Hold\'em|Omaha\sHi|Omaha\sH/L|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi)) - ''', re.VERBOSE) + ''' % substitutions, re.VERBOSE) re_SplitHands = re.compile(r"\n\n+") re_TailSplitHands = re.compile(r"(\n\n+)") re_HandInfo = re.compile(r'''.*\#(?P[0-9]+):\s @@ -51,29 +56,29 @@ class Fulltilt(HandHistoryConverter): (?PPlay\sChip\s|PC)? (?P[-\s\da-zA-Z]+)\s (\((?P.+)\)\s)?-\s - \$?(?P[.0-9]+)/\$?(?P[.0-9]+)\s(Ante\s\$?(?P[.0-9]+)\s)?-\s - \$?(?P[.0-9]+\sCap\s)? + [%(LS)s]?(?P[.0-9]+)/[%(LS)s]?(?P[.0-9]+)\s(Ante\s[%(LS)s]?(?P[.0-9]+)\s)?-\s + [%(LS)s]?(?P[.0-9]+\sCap\s)? (?P[a-zA-Z\/\'\s]+)\s-\s (?P\d+:\d+:\d+\s(?P\w+)\s-\s\d+/\d+/\d+|\d+:\d+\s(?P\w+)\s-\s\w+\,\s\w+\s\d+\,\s\d+) (?P\(partial\))?\n (?:.*?\n(?PHand\s\#(?P=HID)\shas\sbeen\scanceled))? - ''', re.VERBOSE|re.DOTALL) + ''' % substitutions, re.VERBOSE|re.DOTALL) re_TourneyExtraInfo = re.compile('''(((?P[^$]+)? - (?P\$)?(?P[.0-9]+)?\s*\+\s*\$?(?P[.0-9]+)? + (?P[%(LS)s])?(?P[.0-9]+)?\s*\+\s*[%(LS)s]?(?P[.0-9]+)? (\s(?P(KO|Heads\sUp|Matrix\s\dx|Rebuy|Madness)))? (\s(?PShootout))? (\s(?PSit\s&\sGo))? (\s\((?PTurbo)\))?)|(?P.+)) - ''', re.VERBOSE) + ''' % substitutions, re.VERBOSE) re_Button = re.compile('^The button is in seat #(?P+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index ee6d0bf2..3ca578e0 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -79,13 +79,13 @@ class Hand(object): self.fee = None # the Database code is looking for this one .. ? self.level = None self.mixed = None - self.speed = None - self.isRebuy = None - self.isAddOn = None - self.isKO = None + self.speed = "Normal" + self.isRebuy = False + self.isAddOn = False + self.isKO = False self.koBounty = None - self.isMatrix = None - self.isShootout = None + self.isMatrix = False + self.isShootout = False self.added = None self.addedCurrency = None self.tourneyComment = None @@ -683,9 +683,15 @@ class HoldemOmahaHand(Hand): hhc.readPlayerStacks(self) hhc.compilePlayerRegexs(self) hhc.markStreets(self) + if self.cancelled: return - hhc.readBlinds(self) + + try: hhc.readBlinds(self) + except: + print "*** Parse error reading blinds (check compilePlayerRegexs as a likely culprit)", self + return + hhc.readAntes(self) hhc.readButton(self) hhc.readHeroCards(self) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 8faf5dfb..2adf6f13 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -41,9 +41,9 @@ import Hand from Exceptions import FpdbParseError import Configuration -import gettext -gettext.install('fpdb') - +#import gettext +#trans=gettext.translation("fpdb", "/home/steffen/poker/fpdb-dev/pyfpdb/locale", languages=["de"]) +#trans.install() import pygtk import gtk @@ -441,6 +441,7 @@ or None if we fail to get the info """ pass else: print "unable to read file with any codec in list!", self.in_path + self.obs = "" elif self.filetype == "xml": doc = xml.dom.minidom.parse(filename) self.doc = doc diff --git a/pyfpdb/ImapFetcher.py b/pyfpdb/ImapFetcher.py index da13698c..97dbf3de 100755 --- a/pyfpdb/ImapFetcher.py +++ b/pyfpdb/ImapFetcher.py @@ -33,16 +33,16 @@ 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) + #print "useSSL",config.useSsl,"host",config.host + if config.useSsl: + server = IMAP4_SSL(config.host) else: - server = IMAP4(config.email.host) - response = server.login(config.email.username, config.email.password) #TODO catch authentication error + server = IMAP4(config.host) + response = server.login(config.username, config.password) #TODO catch authentication error print "response to logging in:",response #print "server.list():",server.list() #prints list of folders - response = server.select(config.email.folder) + response = server.select(config.folder) #print "response to selecting INBOX:",response if response[0]!="OK": raise error #TODO: show error message diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index 9c665bd3..2c3c4bd2 100755 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -39,8 +39,8 @@ class FpdbParseError(FpdbParseError): class PartyPoker(HandHistoryConverter): sitename = "PartyPoker" codepage = "cp1252" - siteId = 9 - filetype = "text" + siteId = 9 + filetype = "text" sym = {'USD': "\$", } # Static regexes @@ -96,7 +96,7 @@ class PartyPoker(HandHistoryConverter): re_NoSmallBlind = re.compile( '^There is no Small Blind in this hand as the Big Blind ' 'of the previous hand left the table', re.MULTILINE) - + re_20BBmin = re.compile(r"Table 20BB Min") def allHandsAsList(self): list = HandHistoryConverter.allHandsAsList(self) @@ -185,6 +185,7 @@ class PartyPoker(HandHistoryConverter): info = {} m = self._getGameType(handText) + m_20BBmin = self.re_20BBmin.search(handText) if m is None: return None @@ -216,7 +217,18 @@ class PartyPoker(HandHistoryConverter): info['type'] = 'ring' if info['type'] == 'ring': - info['sb'], info['bb'] = ringBlinds(mg['RINGLIMIT']) + if m_20BBmin is None: + bb = float(mg['RINGLIMIT'])/100.0 + else: + bb = float(mg['RINGLIMIT'])/40.0 + + if bb == 0.25: + sb = 0.10 + else: + sb = bb/2.0 + + info['bb'] = "%.2f" % (bb) + info['sb'] = "%.2f" % (sb) info['currency'] = currencies[mg['CURRENCY']] else: info['sb'] = clearMoneyString(mg['SB']) @@ -291,9 +303,9 @@ class PartyPoker(HandHistoryConverter): if key == 'TABLE': hand.tablename = info[key] if key == 'MTTTABLE': - if info[key] != None: - hand.tablename = info[key] - hand.tourNo = info['TABLE'] + if info[key] != None: + hand.tablename = info[key] + hand.tourNo = info['TABLE'] if key == 'BUTTON': hand.buttonpos = info[key] if key == 'TOURNO': @@ -483,13 +495,6 @@ class PartyPoker(HandHistoryConverter): print 'party', 'getTableTitleRe', table_number return table_name - -def ringBlinds(ringLimit): - "Returns blinds for current limit in cash games" - ringLimit = float(clearMoneyString(ringLimit)) - if ringLimit == 5.: ringLimit = 4. - return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) ) - def clearMoneyString(money): "Renders 'numbers' like '1 200' and '2,000'" return money.replace(' ', '').replace(',', '') diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 86334bd5..52d6682d 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -47,9 +47,13 @@ class PokerStars(HandHistoryConverter): '0.40': ('0.10', '0.20'), '0.50': ('0.10', '0.25'), '1.00': ('0.25', '0.50'), '2.00': ('0.50', '1.00'), '2': ('0.50', '1.00'), '4' : ('1.00', '2.00'), '4.00': ('1.00', '2.00'), '6': ('1.00', '3.00'), '6.00': ('1.00', '3.00'), - '10.00': ('2.00', '5.00'), '20.00': ('5.00', '10.00'), '30.00': ('10.00', '15.00'), - '60.00': ('15.00', '30.00'), '100.00': ('25.00', '50.00'), '200.00': ('50.00', '100.00'), - '400.00': ('100.00', '200.00'), '1000.00': ('250.00', '500.00')} + '10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'), '20.00': ('5.00', '10.00'), + '20': ('5.00', '10.00'), '30.00': ('10.00', '15.00'), '30': ('10.00', '15.00'), + '60.00': ('15.00', '30.00'), '60': ('15.00', '30.00'), '100.00': ('25.00', '50.00'), + '100': ('25.00', '50.00'),'200.00': ('50.00', '100.00'), '200': ('50.00', '100.00'), + '400.00': ('100.00', '200.00'), '400': ('100.00', '200.00'),'1000.00': ('250.00', '500.00'), + '1000': ('250.00', '500.00') + } limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl', 'LIMIT':'fl' } games = { # base, category @@ -74,7 +78,7 @@ class PokerStars(HandHistoryConverter): # here's how I plan to use LS (?P(?P[%(LS)s\d\.]+)?\+?(?P[%(LS)s\d\.]+)?\+?(?P[%(LS)s\d\.]+)?\s?(?P%(LEGAL_ISO)s)?|Freeroll)\s+)? # close paren of tournament info - (?PHORSE|8\-Game|HOSE)?\s?\(? + (?PHORSE|8\-Game|HOSE|Mixed PLH/PLO)?\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 (-\s)? diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 73505129..7f8c4108 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -137,21 +137,21 @@ class Sql: 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), + tourneysPlayersId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (tourneysPlayersId) 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), + tourneysPlayersId INT NOT NULL, FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id), playerId INT NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), buyInPercentage FLOAT NOT NULL, payOffPercentage FLOAT NOT NULL)""" elif db_server == 'sqlite': self.query['createBackingsTable'] = """CREATE TABLE Backings ( id INTEGER PRIMARY KEY, - tourneysPlayerId INT NOT NULL, + tourneysPlayersId INT NOT NULL, playerId INT NOT NULL, buyInPercentage REAL UNSIGNED NOT NULL, payOffPercentage REAL UNSIGNED NOT NULL)""" @@ -543,6 +543,7 @@ class Sql: position CHAR(1), seatNo SMALLINT NOT NULL, sitout BOOLEAN NOT NULL, + wentAllInOnStreet SMALLINT, card1 smallint NOT NULL, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ card2 smallint NOT NULL, @@ -659,6 +660,7 @@ class Sql: position CHAR(1), seatNo SMALLINT NOT NULL, sitout BOOLEAN NOT NULL, + wentAllInOnStreet SMALLINT, card1 smallint NOT NULL, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ card2 smallint NOT NULL, @@ -774,6 +776,7 @@ class Sql: position TEXT, seatNo INT NOT NULL, sitout BOOLEAN NOT NULL, + wentAllInOnStreet INT, card1 INT NOT NULL, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ card2 INT NOT NULL, @@ -915,7 +918,7 @@ class Sql: commentTs timestamp without time zone)""" elif db_server == 'sqlite': self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers ( - id INT PRIMARY KEY, + id INTEGER PRIMARY KEY, tourneyId INT, playerId INT, rank INT, @@ -960,7 +963,7 @@ class Sql: commentTs timestamp without time zone)""" elif db_server == 'sqlite': self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions ( - id INT PRIMARY KEY, + id INTEGER PRIMARY KEY, handsPlayerId BIGINT, street SMALLINT, actionNo SMALLINT, @@ -1333,8 +1336,6 @@ class Sql: and (p.siteId = %s or %s = -1) """ - self.query['getSiteId'] = """SELECT id from Sites where name = %s""" - self.query['get_stats_from_hand'] = """ SELECT hc.playerId AS player_id, hp.seatNo AS seat, @@ -1397,6 +1398,7 @@ class Sql: sum(hc.foldToStreet4CBChance) AS f_cb_opp_4, sum(hc.foldToStreet4CBDone) AS f_cb_4, sum(hc.totalProfit) AS net, + sum(gt.bigblind) AS bigblind, sum(hc.street1CheckCallRaiseChance) AS ccr_opp_1, sum(hc.street1CheckCallRaiseDone) AS ccr_1, sum(hc.street2CheckCallRaiseChance) AS ccr_opp_2, @@ -1425,6 +1427,7 @@ class Sql: INNER JOIN HudCache hc ON ( hc.PlayerId = hp.PlayerId+0 AND hc.gametypeId+0 = h.gametypeId+0) INNER JOIN Players p ON (p.id = hp.PlayerId+0) + INNER JOIN Gametypes gt ON (gt.id = hc.gametypeId) WHERE h.id = %s AND hc.styleKey > %s /* styleKey is currently 'd' (for date) followed by a yyyymmdd @@ -1496,6 +1499,7 @@ class Sql: sum(hc.foldToStreet4CBChance) AS f_cb_opp_4, sum(hc.foldToStreet4CBDone) AS f_cb_4, sum(hc.totalProfit) AS net, + sum(gt.bigblind) AS bigblind, sum(hc.street1CheckCallRaiseChance) AS ccr_opp_1, sum(hc.street1CheckCallRaiseDone) AS ccr_1, sum(hc.street2CheckCallRaiseChance) AS ccr_opp_2, @@ -1523,6 +1527,7 @@ class Sql: INNER JOIN HandsPlayers hp ON (hp.handId = h.id) INNER JOIN HudCache hc ON (hc.playerId = hp.playerId) INNER JOIN Players p ON (p.id = hc.playerId) + INNER JOIN Gametypes gt ON (gt.id = hc.gametypeId) WHERE h.id = %s AND ( /* 2 separate parts for hero and opponents */ ( hp.playerId != %s @@ -1622,6 +1627,7 @@ class Sql: cast(hp2.foldToStreet4CBChance as integer) AS f_cb_opp_4, cast(hp2.foldToStreet4CBDone as integer) AS f_cb_4, cast(hp2.totalProfit as integer) AS net, + cast(gt.bigblind as integer) AS bigblind, cast(hp2.street1CheckCallRaiseChance as integer) AS ccr_opp_1, cast(hp2.street1CheckCallRaiseDone as integer) AS ccr_1, cast(hp2.street2CheckCallRaiseChance as integer) AS ccr_opp_2, @@ -1651,6 +1657,7 @@ class Sql: INNER JOIN HandsPlayers hp ON (h.id = hp.handId) /* players in this hand */ INNER JOIN HandsPlayers hp2 ON (hp2.playerId+0 = hp.playerId+0 AND (hp2.handId = h2.id+0)) /* other hands by these players */ INNER JOIN Players p ON (p.id = hp2.PlayerId+0) + INNER JOIN Gametypes gt ON (gt.id = h2.gametypeId) WHERE hp.handId = %s /* check activeseats once this data returned (don't want to do that here as it might assume a session ended just because the number of seats dipped for a few hands) @@ -1724,6 +1731,7 @@ class Sql: cast(hp2.foldToStreet4CBChance as integer) AS f_cb_opp_4, cast(hp2.foldToStreet4CBDone as integer) AS f_cb_4, cast(hp2.totalProfit as integer) AS net, + cast(gt.bigblind as integer) AS bigblind, cast(hp2.street1CheckCallRaiseChance as integer) AS ccr_opp_1, cast(hp2.street1CheckCallRaiseDone as integer) AS ccr_1, cast(hp2.street2CheckCallRaiseChance as integer) AS ccr_opp_2, @@ -1754,6 +1762,7 @@ class Sql: INNER JOIN HandsPlayers hp2 ON ( hp2.playerId+0 = hp.playerId+0 AND hp2.handId = h2.id) /* other hands by these players */ INNER JOIN Players p ON (p.id = hp2.PlayerId+0) + INNER JOIN Gametypes gt ON (gt.id = h2.gametypeId) WHERE h.id = %s /* check activeseats once this data returned (don't want to do that here as it might assume a session ended just because the number of seats dipped for a few hands) @@ -1827,6 +1836,7 @@ class Sql: cast(hp2.foldToStreet4CBChance as integer) AS f_cb_opp_4, cast(hp2.foldToStreet4CBDone as integer) AS f_cb_4, cast(hp2.totalProfit as integer) AS net, + cast(gt.bigblind as integer) AS bigblind, cast(hp2.street1CheckCallRaiseChance as integer) AS ccr_opp_1, cast(hp2.street1CheckCallRaiseDone as integer) AS ccr_1, cast(hp2.street2CheckCallRaiseChance as integer) AS ccr_opp_2, @@ -1857,6 +1867,7 @@ class Sql: INNER JOIN HandsPlayers hp2 ON ( hp2.playerId+0 = hp.playerId+0 AND hp2.handId = h2.id) /* other hands by these players */ INNER JOIN Players p ON (p.id = hp2.PlayerId+0) + INNER JOIN Gametypes gt ON (gt.id = h2.gametypeId) WHERE h.id = %s /* check activeseats once this data returned (don't want to do that here as it might assume a session ended just because the number of seats dipped for a few hands) @@ -1997,8 +2008,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""" #self.query['getLimits'] = already defined further up self.query['getLimits2'] = """SELECT DISTINCT type, limitType, bigBlind from Gametypes @@ -2016,6 +2025,7 @@ class Sql: , limitType , bigBlind as bb_or_buyin from Gametypes gt + WHERE type = 'ring' order by type, limitType DESC, bb_or_buyin DESC""" if db_server == 'mysql': @@ -2389,7 +2399,7 @@ class Sql: 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 + ,(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 @@ -2397,11 +2407,11 @@ class Sql: ,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 rank = 1 THEN 1 ELSE 0 END) AS _1st + ,SUM(CASE WHEN rank = 2 THEN 1 ELSE 0 END) AS _2nd + ,SUM(CASE WHEN rank = 3 THEN 1 ELSE 0 END) AS _3rd ,SUM(tp.winnings)/100.0 AS won - ,SUM(CASE WHEN tt.currency = "USD" THEN (tt.buyIn+tt.fee)/100.0 ELSE tt.buyIn END) AS spent + ,SUM(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 @@ -2416,9 +2426,72 @@ class Sql: ,playerName ,siteName""" elif db_server == 'postgresql': - self.query['tourneyPlayerDetailedStats'] = """TODO""" + # sc: itm and profitPerTourney changed to "ELSE 0" to avoid divide by zero error as temp fix + # proper fix should use coalesce() or case ... when ... to work in all circumstances + self.query['tourneyPlayerDetailedStats'] = """ + select s.name AS siteName + ,t.tourneyTypeId AS tourneyTypeId + ,tt.currency AS currency + ,(CASE WHEN tt.currency = 'USD' THEN tt.buyIn/100.0 ELSE tt.buyIn END) AS buyIn + ,tt.fee/100.0 AS fee + ,tt.category AS category + ,tt.limitType AS limitType + ,p.name AS playerName + ,COUNT(1) AS tourneyCount + ,SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END) AS unknownRank + ,SUM(CASE WHEN winnings > 0 THEN 1 ELSE 0 END) + /(COUNT(1) - SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 0 END)) AS itm + ,SUM(CASE WHEN rank = 1 THEN 1 ELSE 0 END) AS _1st + ,SUM(CASE WHEN rank = 2 THEN 1 ELSE 0 END) AS _2nd + ,SUM(CASE WHEN rank = 3 THEN 1 ELSE 0 END) AS _3rd + ,SUM(tp.winnings)/100.0 AS won + ,SUM(CASE WHEN tt.currency = 'USD' THEN (tt.buyIn+tt.fee)/100.0 ELSE tt.buyIn END) AS spent + ,SUM(tp.winnings)/SUM(tt.buyin+tt.fee)*100.0-100 AS roi + ,SUM(tp.winnings-(tt.buyin+tt.fee))/100.0 + /(COUNT(1)-SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 0 END)) AS profitPerTourney + from TourneysPlayers tp + inner join Tourneys t on (t.id = tp.tourneyId) + inner join TourneyTypes tt on (tt.Id = t.tourneyTypeId) + inner join Sites s on (s.Id = tt.siteId) + inner join Players p on (p.Id = tp.playerId) + where tp.playerId in + and to_char(t.startTime, 'YYYY-MM-DD HH24:MI:SS') + group by tourneyTypeId, s.name, playerName, tt.currency, tt.buyin, tt.fee + , tt.category, tt.limitType + order by tourneyTypeId + ,playerName + ,siteName""" elif db_server == 'sqlite': - self.query['tourneyPlayerDetailedStats'] = """TODO""" + self.query['tourneyPlayerDetailedStats'] = """ + select s.name AS siteName + ,t.tourneyTypeId AS tourneyTypeId + ,tt.currency AS currency + ,(CASE WHEN tt.currency = 'USD' THEN tt.buyIn/100.0 ELSE tt.buyIn END) AS buyIn + ,tt.fee/100.0 AS fee + ,tt.category AS category + ,tt.limitType AS limitType + ,p.name AS playerName + ,COUNT(1) AS tourneyCount + ,SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END) AS unknownRank + ,SUM(CASE WHEN winnings > 0 THEN 1 ELSE 0 END)/(COUNT(1) - SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END)) AS itm + ,SUM(CASE WHEN rank = 1 THEN 1 ELSE 0 END) AS _1st + ,SUM(CASE WHEN rank = 2 THEN 1 ELSE 0 END) AS _2nd + ,SUM(CASE WHEN rank = 3 THEN 1 ELSE 0 END) AS _3rd + ,SUM(tp.winnings)/100.0 AS won + ,SUM(CASE WHEN tt.currency = 'USD' THEN (tt.buyIn+tt.fee)/100.0 ELSE tt.buyIn END) AS spent + ,SUM(tp.winnings)/SUM(tt.buyin+tt.fee)*100.0-100 AS roi + ,SUM(tp.winnings-(tt.buyin+tt.fee))/100.0/(COUNT(1)-SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END)) AS profitPerTourney + from TourneysPlayers tp + inner join Tourneys t on (t.id = tp.tourneyId) + inner join TourneyTypes tt on (tt.Id = t.tourneyTypeId) + inner join Sites s on (s.Id = tt.siteId) + inner join Players p on (p.Id = tp.playerId) + where tp.playerId in + and datetime(t.startTime) + group by tourneyTypeId, playerName + order by tourneyTypeId + ,playerName + ,siteName""" if db_server == 'mysql': self.query['playerStats'] = """ @@ -2767,6 +2840,8 @@ class Sql: order by stats.category, stats.limitType, stats.bigBlindDesc desc , cast(stats.PlPosition as signed) """ + elif db_server == 'sqlite': + self.query['playerStatsByPosition'] = ""#TODO else: # assume postgresql self.query['playerStatsByPosition'] = """ select /* stats from hudcache */ @@ -2937,7 +3012,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 gt.type is 'ring' + AND gt.type LIKE 'ring' ORDER by time""" elif db_server == 'postgresql': self.query['sessionStats'] = """ @@ -2949,7 +3024,7 @@ class Sql: INNER JOIN Players p on (p.Id = hp.playerId) WHERE hp.playerId in AND h.startTime - AND gt.type is 'ring' + AND gt.type LIKE 'ring' ORDER by time""" elif db_server == 'sqlite': self.query['sessionStats'] = """ @@ -2978,7 +3053,7 @@ class Sql: ,playerId ,activeSeats ,position - ,tourneyTypeId + ,styleKey ,HDs ,wonWhenSeenStreet1 @@ -3068,7 +3143,7 @@ class Sql: when hp.position = '9' then 'E' else 'E' end AS hc_position - ,t.tourneyTypeId + ,date_format(h.startTime, 'd%y%m%d') ,count(1) ,sum(wonWhenSeenStreet1) @@ -3142,14 +3217,13 @@ class Sql: ,sum(hp.street4Raises) FROM HandsPlayers hp INNER JOIN Hands h ON (h.id = hp.handId) - INNER JOIN TourneysPlayers tp ON (tp.id = hp.tourneysPlayersId) - INNER JOIN Tourneys t ON (t.id = tp.tourneyId) + GROUP BY h.gametypeId ,hp.playerId ,h.seats ,hc_position - ,t.tourneyTypeId + ,date_format(h.startTime, 'd%y%m%d') """ elif db_server == 'postgresql': @@ -3159,7 +3233,7 @@ class Sql: ,playerId ,activeSeats ,position - ,tourneyTypeId + ,styleKey ,HDs ,wonWhenSeenStreet1 @@ -3249,7 +3323,7 @@ class Sql: when hp.position = '9' then 'E' else 'E' end AS hc_position - ,t.tourneyTypeId + ,'d' || to_char(h.startTime, 'YYMMDD') ,count(1) ,sum(wonWhenSeenStreet1) @@ -3323,14 +3397,13 @@ class Sql: ,sum(CAST(hp.street4Raises as integer)) FROM HandsPlayers hp INNER JOIN Hands h ON (h.id = hp.handId) - INNER JOIN TourneysPlayers tp ON (tp.id = hp.tourneysPlayersId) - INNER JOIN Tourneys t ON (t.id = tp.tourneyId) + GROUP BY h.gametypeId ,hp.playerId ,h.seats ,hc_position - ,t.tourneyTypeId + ,to_char(h.startTime, 'YYMMDD') """ else: # assume sqlite @@ -3340,7 +3413,7 @@ class Sql: ,playerId ,activeSeats ,position - ,tourneyTypeId + ,styleKey ,HDs ,wonWhenSeenStreet1 @@ -3430,7 +3503,7 @@ class Sql: when hp.position = '9' then 'E' else 'E' end AS hc_position - ,t.tourneyTypeId + ,'d' || substr(strftime('%Y%m%d', h.startTime),3,7) ,count(1) ,sum(wonWhenSeenStreet1) @@ -3504,14 +3577,13 @@ class Sql: ,sum(CAST(hp.street4Raises as integer)) FROM HandsPlayers hp INNER JOIN Hands h ON (h.id = hp.handId) - INNER JOIN TourneysPlayers tp ON (tp.id = hp.tourneysPlayersId) - INNER JOIN Tourneys t ON (t.id = tp.tourneyId) + GROUP BY h.gametypeId ,hp.playerId ,h.seats ,hc_position - ,t.tourneyTypeId + ,'d' || substr(strftime('%Y%m%d', h.startTime),3,7) """ @@ -3798,6 +3870,22 @@ class Sql: WHERE tt.siteId=%s AND t.siteTourneyNo=%s """ + self.query['getTourneyInfo'] = """SELECT tt.*, t.* + FROM Tourneys t + INNER JOIN TourneyTypes tt ON (t.tourneyTypeId = tt.id) + INNER JOIN Sites s ON (tt.siteId = s.id) + WHERE s.name=%s AND t.siteTourneyNo=%s + """ + + self.query['getTourneyPlayerInfo'] = """SELECT tp.* + FROM Tourneys t + INNER JOIN TourneyTypes tt ON (t.tourneyTypeId = tt.id) + INNER JOIN Sites s ON (tt.siteId = s.id) + INNER JOIN TourneysPlayers tp ON (tp.tourneyId = t.id) + INNER JOIN Players p ON (p.id = tp.playerId) + WHERE s.name=%s AND t.siteTourneyNo=%s AND p.name=%s + """ + self.query['insertTourney'] = """INSERT INTO Tourneys (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime, endTime, tourneyName, matrixIdProcessed, diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py index 74558da8..e38b5806 100755 --- a/pyfpdb/Stats.py +++ b/pyfpdb/Stats.py @@ -48,7 +48,7 @@ # 6 For each stat you make add a line to the __main__ function to test it. # Standard Library modules -#import sys +import sys # pyGTK modules import pygtk @@ -60,6 +60,10 @@ import Configuration import Database import Charset +import logging +# logging has been set up in fpdb.py or HUD_main.py, use their settings: +log = logging.getLogger("db") + re_Places = re.compile("_[0-9]$") re_Percent = re.compile("%$") @@ -79,12 +83,23 @@ def do_stat(stat_dict, player = 24, stat = 'vpip'): else: base = stat[0:-2] places = int(stat[-1:]) - result = eval("%(stat)s(stat_dict, %(player)d)" % {'stat': base, 'player': player}) + result = (0.0, '0.0', 'notset=0', 'notset=0', '0', 'not set') + try: + result = eval("%(stat)s(stat_dict, %(player)d)" % {'stat': base, 'player': player}) + except: + pass # + log.info("exception getting stat "+base+" for player "+str(player)+str(sys.exc_info())) + log.debug("result = %s" % str(result) ) + match = re_Percent.search(result[1]) - if match is None: - result = (result[0], "%.*f" % (places, result[0]), result[2], result[3], result[4], result[5]) - else: - result = (result[0], "%.*f%%" % (places, 100*result[0]), result[2], result[3], result[4], result[5]) + try: + if match is None: + result = (result[0], "%.*f" % (places, result[0]), result[2], result[3], result[4], result[5]) + else: + result = (result[0], "%.*f%%" % (places, 100*result[0]), result[2], result[3], result[4], result[5]) + except: + log.info( "error: %s" % str(sys.exc_info())) + raise return result # OK, for reference the tuple returned by the stat is: @@ -198,7 +213,7 @@ def wmsd(stat_dict, player): ) def profit100(stat_dict, player): - """ Profit won per 100 hands (no decimal places).""" + """ Profit won per 100 hands.""" stat = 0.0 try: stat = float(stat_dict[player]['net'])/float(stat_dict[player]['n']) @@ -212,13 +227,57 @@ def profit100(stat_dict, player): except: print "exception calcing p/100: 100 * %d / %d" % (stat_dict[player]['net'], stat_dict[player]['n']) return (stat, - '%.0f' % (0), - 'p=%.0f' % (0), - 'p/100=%.0f' % (0), + '%.0f' % (0.0), + 'p=%.0f' % (0.0), + 'p/100=%.0f' % (0.0), '(%d/%d)' % (0, 0), 'profit/100hands' ) +def bbper100(stat_dict, player): + """ big blinds won per 100 hands.""" + stat = 0.0 + try: + stat = 100.0 * float(stat_dict[player]['net']) / float(stat_dict[player]['bigblind']) + return (stat, + '%5.3f' % (stat), + 'bb100=%5.3f' % (stat), + 'bb100=%5.3f' % (stat), + '(%d,%d)' % (100*stat_dict[player]['net'],stat_dict[player]['bigblind']), + 'big blinds/100 hands' + ) + except: + log.info("exception calcing bb/100: "+str(stat_dict[player])) + return (stat, + '%.0f' % (0), + 'bb100=%.0f' % (0), + 'bb100=%.0f' % (0), + '(%f)' % (0), + 'big blinds/100 hands' + ) + +def BBper100(stat_dict, player): + """ Big Bets won per 100 hands.""" + stat = 0.0 + try: + stat = 50 * float(stat_dict[player]['net']) / float(stat_dict[player]['bigblind']) + return (stat, + '%5.3f' % (stat), + 'BB100=%5.3f' % (stat), + 'BB100=%5.3f' % (stat), + '(%d,%d)' % (100*stat_dict[player]['net'],2*stat_dict[player]['bigblind']), + 'Big Bets/100 hands' + ) + except: + log.info("exception calcing BB/100: "+str(stat_dict[player])) + return (stat, + '%.0f' % (0.0), + 'BB100=%.0f' % (0.0), + 'BB100=%.0f' % (0.0), + '(%f)' % (0.0), + 'Big Bets/100 hands' + ) + def saw_f(stat_dict, player): """ Saw flop/4th.""" try: @@ -711,10 +770,10 @@ def ffreq1(stat_dict, player): ) except: return (stat, - '%3.1f' % (0) + '%', - 'ff1=%3.1f' % (0) + '%', - 'ff_1=%3.1f' % (0) + '%', - '(%d/%d)' % (0, 0), + 'NA', + 'ff1=NA', + 'ff_1=NA', + '(0/0)', '% fold frequency flop/4th' ) @@ -782,13 +841,27 @@ def ffreq4(stat_dict, player): ) if __name__== "__main__": + statlist = dir() + misslist = [ "Configuration", "Database", "Charset", "codecs", "encoder" + , "do_stat", "do_tip", "GInitiallyUnowned", "gtk", "pygtk" + , "re", "re_Percent", "re_Places" + ] + statlist = [ x for x in statlist if x not in dir(sys) ] + statlist = [ x for x in statlist if x not in dir(codecs) ] + statlist = [ x for x in statlist if x not in misslist ] + #print "statlist is", statlist + c = Configuration.Config() #TODO: restore the below code. somehow it creates a version 119 DB but commenting this out makes it print a stat list - #db_connection = Database.Database(c) - #h = db_connection.get_last_hand() - #stat_dict = db_connection.get_stats_from_hand(h, "ring") + db_connection = Database.Database(c) + h = db_connection.get_last_hand() + stat_dict = db_connection.get_stats_from_hand(h, "ring") - #for player in stat_dict.keys(): + for player in stat_dict.keys(): + print "Example stats, player =", player, "hand =", h, ":" + for attr in statlist: + print " ", do_stat(stat_dict, player=player, stat=attr) + break #print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip') #print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr') #print "player = ", player, do_stat(stat_dict, player = player, stat = 'wtsd') @@ -820,11 +893,7 @@ if __name__== "__main__": print "\n\nLegal stats:" print "(add _0 to name to display with 0 decimal places, _1 to display with 1, etc)\n" - for attr in dir(): - if attr.startswith('__'): continue - if attr in ("Configuration", "Database", "GInitiallyUnowned", "gtk", "pygtk", - "player", "c", "db_connection", "do_stat", "do_tip", "stat_dict", - "h", "re", "re_Percent", "re_Places"): continue + for attr in statlist: print "%-14s %s" % (attr, eval("%s.__doc__" % (attr))) # print " " % (attr) print diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index d76eb62f..df75347b 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -20,6 +20,10 @@ import sys import re import Queue +#import gettext +#trans=gettext.translation("fpdb", "locale", ["en_GB"]) +#trans.install() + # if path is set to use an old version of python look for a new one: # (does this work in linux?) if os.name == 'nt' and sys.version[0:3] not in ('2.5', '2.6') and '-r' not in sys.argv: @@ -103,9 +107,10 @@ import GuiPrefs import GuiLogView import GuiDatabase import GuiBulkImport -import ImapFetcher +import GuiImapFetcher import GuiRingPlayerStats import GuiTourneyPlayerStats +import GuiTourneyViewer import GuiPositionalStats import GuiAutoImport import GuiGraphViewer @@ -785,7 +790,7 @@ class fpdb: - + @@ -794,6 +799,7 @@ class fpdb: + @@ -826,14 +832,15 @@ 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), + ('imapimport', None, '_Import through eMail/IMAP', 'I', 'Import through eMail/IMAP', self.tab_imap_import), ('viewers', None, '_Viewers'), ('autoimp', None, '_Auto Import and HUD', 'A', 'Auto Import and HUD', self.tab_auto_import), ('hudConfigurator', None, '_HUD Configurator', 'H', 'HUD Configurator', self.diaHudConfigurator), ('graphs', None, '_Graphs', 'G', 'Graphs', self.tabGraphViewer), ('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, mysql only)', 'T', 'Tourney Player Stats (tabulated view, mysql only)', self.tab_tourney_player_stats), - ('posnstats', None, 'P_ositional Stats (tabulated view)', 'O', 'Positional Stats (tabulated view)', self.tab_positional_stats), + ('tourneyplayerstats', None, '_Tourney Player Stats (tabulated view)', 'T', 'Tourney Player Stats (tabulated view, mysql only)', self.tab_tourney_player_stats), + ('tourneyviewer', None, 'Tourney _Viewer', None, 'Tourney Viewer)', self.tab_tourney_viewer_stats), + ('posnstats', None, 'P_ositional Stats (tabulated view, not on sqlite)', 'O', 'Positional Stats (tabulated view)', self.tab_positional_stats), ('sessionstats', None, 'Session Stats', None, 'Session Stats', self.tab_session_stats), ('database', None, '_Database'), ('maintaindbs', None, '_Maintain Databases', None, 'Maintain Databases', self.dia_maintain_dbs), @@ -857,10 +864,6 @@ class fpdb: return menubar #end def get_menu - def import_imap_summaries(self, widget, data=None): - result=ImapFetcher.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.""" @@ -891,7 +894,7 @@ class fpdb: self.settings.update(self.config.get_import_parameters()) self.settings.update(self.config.get_default_paths()) - if self.db is not None and self.db.connected: + if self.db is not None and self.db.is_connected(): self.db.disconnect() self.sql = SQL.Sql(db_server = self.settings['db-server']) @@ -914,6 +917,8 @@ class fpdb: if err_msg is not None: self.db = None self.warning_box(err_msg) + if self.db is not None and not self.db.is_connected(): + self.db = None # except FpdbMySQLFailedError: # self.warning_box("Unable to connect to MySQL! Is the MySQL server running?!", "FPDB ERROR") @@ -954,7 +959,7 @@ class fpdb: self.main_vbox.pack_end(self.status_bar, False, True, 0) self.status_bar.show() - if self.db is not None and self.db.connected: + if self.db is not None and self.db.is_connected(): self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.database, self.db.host)) # rollback to make sure any locks are cleared: @@ -988,12 +993,12 @@ class fpdb: if self.db!=None: if self.db.backend==self.db.MYSQL_INNODB: try: - if self.db is not None and self.db.connected(): + if self.db is not None and self.db.is_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(): + if self.db is not None and self.db.is_connected(): self.db.disconnect() else: pass @@ -1021,6 +1026,13 @@ class fpdb: bulk_tab=new_import_thread.get_vbox() self.add_and_display_tab(bulk_tab, "Bulk Import") + def tab_imap_import(self, widget, data=None): + new_thread = GuiImapFetcher.GuiImapFetcher(self.config, self.db, self.sql, self.window) + self.threads.append(new_thread) + tab=new_thread.get_vbox() + self.add_and_display_tab(tab, "IMAP Import") + #end def tab_import_imap_summaries + 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) @@ -1033,6 +1045,12 @@ class fpdb: ps_tab=new_ps_thread.get_vbox() self.add_and_display_tab(ps_tab, "Tourney Player Stats") + def tab_tourney_viewer_stats(self, widget, data=None): + new_thread = GuiTourneyViewer.GuiTourneyViewer(self.config, self.db, self.sql, self.window) + self.threads.append(new_thread) + tab=new_thread.get_vbox() + self.add_and_display_tab(tab, "Tourney Viewer") + def tab_positional_stats(self, widget, data=None): new_ps_thread = GuiPositionalStats.GuiPositionalStats(self.config, self.sql) self.threads.append(new_ps_thread) @@ -1047,14 +1065,17 @@ class fpdb: def tab_main_help(self, widget, data=None): """Displays a tab with the main fpdb help screen""" - mh_tab=gtk.Label("""Welcome to Fpdb! + mh_tab=gtk.Label("""Fpdb needs translators! +If you speak another language and have a few minutes or more to spare get in touch by emailing steffen@schaumburger.info + +Welcome to Fpdb! To be notified of new snapshots and releases go to https://lists.sourceforge.net/lists/listinfo/fpdb-announce and subscribe. If you want to follow development more closely go to https://lists.sourceforge.net/lists/listinfo/fpdb-main and subscribe. 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/. +For documentation please visit our website/wiki 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. diff --git a/pyfpdb/regression-test-files/cash/PartyPoker/Flop/NLHE-USD-0.01-0.02-20100712.emailedHistory.txt b/pyfpdb/regression-test-files/cash/PartyPoker/Flop/NLHE-USD-0.01-0.02-20100712.emailedHistory.txt new file mode 100644 index 00000000..2267fa45 --- /dev/null +++ b/pyfpdb/regression-test-files/cash/PartyPoker/Flop/NLHE-USD-0.01-0.02-20100712.emailedHistory.txt @@ -0,0 +1,41 @@ +***** Hand History For Game 9423586142 ***** +0.01/0.02 Texas Hold'em Game Table (NL) - Mon Jul 12 13:38:32 EDT 2010 +Table 20BB Min Speed #1775757 (Real Money) -- Seat 1 is the button +Total number of players : 9/9 +Seat 1: Player1 ($0.95) +Seat 2: Player2 ($0.57) +Seat 3: Player3 ($0.86) +Seat 4: Player4 ($1.71) +Seat 5: Player5 ($1.76) +Seat 6: Player6 ($0.44) +Seat 7: Player7 ($0.76) +Seat 8: Player8 ($0.68) +Seat 9: Player9 ($0.38) +Player2 posts small blind (0.01) +Player3 posts big blind (0.02) +** Dealing down cards ** +Dealt to Player5 [ Tc, 9d ] +Player5 folds +Player6 calls (0.02) +Player8 folds +Player9 folds +Player1 folds +Player2 calls (0.01) +Player3 raises 0.06 to 0.08 +Player6 calls (0.06) +Player2 folds +** Dealing Flop ** : [ 5s, 7h, 6h ] +Player3 bets (0.13) +Player6 folds +** Summary ** +Main Pot: $0.18 Rake: $0 +Board: [ 5s 7h 6h ] +Player1 balance $0.95, didn't bet (folded) +Player2 balance $0.55, lost $0.02 (folded) +Player3 balance $0.96, bet $0.21, collected $0.31, net +$0.1 +Player4 balance $1.71, sits out +Player5 balance $1.76, didn't bet (folded) +Player6 balance $0.36, lost $0.08 (folded) +Player7 balance $0.76, sits out +Player8 balance $0.68, didn't bet (folded) +Player9 balance $0.38, didn't bet (folded) diff --git a/pyfpdb/regression-test-files/cash/PartyPoker/Flop/NLHE-USD-0.01-0.02-20100731.blindIsForcedAllIn.txt b/pyfpdb/regression-test-files/cash/PartyPoker/Flop/NLHE-USD-0.01-0.02-20100731.blindIsForcedAllIn.txt new file mode 100644 index 00000000..489dbe5e --- /dev/null +++ b/pyfpdb/regression-test-files/cash/PartyPoker/Flop/NLHE-USD-0.01-0.02-20100731.blindIsForcedAllIn.txt @@ -0,0 +1,63 @@ +Game #9485557849 starts. + +#Game No : 9485557849 +***** Hand History for Game 9485557849 ***** +$0.80 USD NL Texas Hold'em - Saturday, July 31, 13:52:16 EDT 2010 +Table 20BB Min Speed #1770998 (Real Money) +Seat 1 is the button +Total number of players : 4/9 +Seat 3: FErki84 ( $1.64 USD ) +Seat 5: Vandercasses ( $0.01 USD ) +Seat 9: jeremyho888 ( $1.02 USD ) +Seat 1: sergeodem ( $1.20 USD ) +FErki84 posts small blind [$0.01 USD]. +Vandercasses posts big blind [$0.01 USD]. +** Dealing down cards ** +Dealt to FErki84 [ 8h Kc ] +jeremyho888 folds +sergeodem calls [$0.02 USD] +FErki84 calls [$0.01 USD] +** Dealing Flop ** [ Td, 7c, 9h ] +FErki84 checks +sergeodem checks +** Dealing Turn ** [ 3h ] +FErki84 checks +sergeodem checks +** Dealing River ** [ Jc ] +FErki84 bets [$0.04 USD] +sergeodem folds +FErki84 shows [ 8h, Kc ]a straight, Seven to Jack. +Vandercasses doesn't show [ Ts, Jd ]two pairs, Jacks and Tens. +FErki84 wins $0.06 USD from the side pot 1 with a straight, Seven to Jack. +FErki84 wins $0.03 USD from the main pot with a straight, Seven to Jack. +Vandercasses has left the table. + +Game #9498788316 starts. + +#Game No : 9498788316 +***** Hand History for Game 9498788316 ***** +$1.60 USD NL Texas Hold'em - Wednesday, August 04, 15:02:33 EDT 2010 +Table 20BB Min #1847547 (No DP) (Real Money) +Seat 2 is the button +Total number of players : 5/6 +Seat 5: CepguTbIu999 ( $1.60 USD ) +Seat 1: Daytona_955 ( $2.45 USD ) +Seat 4: FErki84 ( $2.18 USD ) +Seat 2: anjl2009 ( $2.80 USD ) +Seat 3: lukeman2 ( $0.01 USD ) +lukeman2 posts small blind [$0.01 USD]. +FErki84 posts big blind [$0.04 USD]. +** Dealing down cards ** +Dealt to FErki84 [ 6s 2c ] +CepguTbIu999 folds +Daytona_955 folds +anjl2009 folds +** Dealing Flop ** [ 9d, Ah, 3h ] +** Dealing Turn ** [ Js ] +** Dealing River ** [ Kc ] +lukeman2 shows [ 5h, 5s ]a pair of Fives. +FErki84 shows [ 6s, 2c ]high card Ace. +FErki84 wins $0.03 USD from the side pot 1 with high card, Ace. +lukeman2 wins $0.02 USD from the main pot with a pair of Fives. +lukeman2 has left the table. + diff --git a/pyfpdb/regression-test-files/results/0001_empty_DB.txt b/pyfpdb/regression-test-files/results/0001_empty_DB.txt index f0381238..7e43b4a0 100644 --- a/pyfpdb/regression-test-files/results/0001_empty_DB.txt +++ b/pyfpdb/regression-test-files/results/0001_empty_DB.txt @@ -1,5 +1,5 @@ fpdb database dump -DB version=136 +DB version=142 ################### Table Autorates @@ -44,7 +44,7 @@ empty table ################### Table Settings ################### - version=136 + version=142 ################### diff --git a/pyfpdb/regression-test-files/tour/PartyPoker/Flop/NLHE-USD-STT-unknownBuyIn-20100713.emailedHistory.txt b/pyfpdb/regression-test-files/tour/PartyPoker/Flop/NLHE-USD-STT-unknownBuyIn-20100713.emailedHistory.txt new file mode 100644 index 00000000..e6afb2c8 --- /dev/null +++ b/pyfpdb/regression-test-files/tour/PartyPoker/Flop/NLHE-USD-STT-unknownBuyIn-20100713.emailedHistory.txt @@ -0,0 +1,37 @@ +***** Hand History For Game 9336845949 ***** +30/60 Tourney Texas Hold'em Game Table (NL) (STT Tournament #52792286) - Sun Jun 13 12:21:39 EDT 2010 +Table 174827 (Real Money) -- Seat 6 is the button +Total number of players : 6/6 +Seat 1: Player1 (1520) +Seat 2: Player2 (1540) +Seat 3: Player3 (2120) +Seat 4: Player4 (2460) +Seat 5: Player5 (2600) +Seat 6: Player6 (1760) +Player1 posts small blind (30) +Player2 posts big blind (60) +** Dealing down cards ** +Dealt to Player5 [ Jc, Js ] +Player3 folds +Player4 folds +Player6 folds +Player1 calls (210) +Player2 folds +** Dealing Flop ** : [ 4h, 7d, 5c ] +Player1 checks +Player1 calls (450) +** Dealing Turn ** : [ Kd ] +Player1 checks +Player1 calls (830) +Player1 is all-In. +** Dealing River ** : [ Jd ] +Creating Main Pot with 3100 with Player1 +** Summary ** +Main Pot: 3100 +Board: [ 4h 7d 5c Kd Jd ] +Player1 balance 0, lost 1520 [ Ks 6c ] [ a pair of kings -- Ks,Kd,Jd,7d,6c ] +Player2 balance 1480, lost 60 (folded) +Player3 balance 2120, didn't bet (folded) +Player4 balance 2460, didn't bet (folded) +Player5 balance 4180, bet 1520, collected 3100, net +1580 [ Jc Js ] [ three of a kind, jacks -- Kd,Jc,Js,Jd,7d ] +Player6 balance 1760, didn't bet (folded)