From 0acaa8cb6df0ec67093100647dc5464150b73d47 Mon Sep 17 00:00:00 2001 From: gimick Date: Tue, 28 Dec 2010 03:20:49 +0000 Subject: [PATCH 01/32] GUI: RingPlayerStats correct the description of w$sd --- pyfpdb/GuiRingPlayerStats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index 86ba1aad..c19f0f1d 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -58,7 +58,7 @@ onlinehelp = {'Game':_('Type of Game'), 'Saw_F':_('% Saw Flop vs hands dealt'), 'SawSD':_('Saw Show Down / River'), 'WtSDwsF':_('Went To Show Down When Saw Flop'), - 'W$SD':_('Amount Won when Show Down seen'), + 'W$SD':_('% Times Won when Show Down seen'), 'FlAFq':_('Flop Aggression\n% Bet or Raise after seeing Flop'), 'TuAFq':_('Turn Aggression\n% Bet or Raise after seeing Turn'), 'RvAFq':_('River Aggression\n% Bet or Raise after seeing River'), From 95b496e9f477fff9e9310cdc808f8d05348e1ff8 Mon Sep 17 00:00:00 2001 From: gimick Date: Tue, 28 Dec 2010 13:35:24 +0000 Subject: [PATCH 02/32] GUI: another w$sd description change --- pyfpdb/GuiRingPlayerStats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index c19f0f1d..f061322e 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -58,7 +58,7 @@ onlinehelp = {'Game':_('Type of Game'), 'Saw_F':_('% Saw Flop vs hands dealt'), 'SawSD':_('Saw Show Down / River'), 'WtSDwsF':_('Went To Show Down When Saw Flop'), - 'W$SD':_('% Times Won when Show Down seen'), + 'W$SD':_('% Won some money at showdown'), 'FlAFq':_('Flop Aggression\n% Bet or Raise after seeing Flop'), 'TuAFq':_('Turn Aggression\n% Bet or Raise after seeing Turn'), 'RvAFq':_('River Aggression\n% Bet or Raise after seeing River'), From 744e5292eff4095752d65d920c1518269284d6ff Mon Sep 17 00:00:00 2001 From: gimick Date: Tue, 28 Dec 2010 18:29:23 +0000 Subject: [PATCH 03/32] py2exe: update walkthrough (suppress popup error message on exit) --- .../windows/py2exeWalkthroughPython26.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packaging/windows/py2exeWalkthroughPython26.txt b/packaging/windows/py2exeWalkthroughPython26.txt index 7ad865c0..2762699e 100644 --- a/packaging/windows/py2exeWalkthroughPython26.txt +++ b/packaging/windows/py2exeWalkthroughPython26.txt @@ -33,6 +33,7 @@ psycopg2 ... http://www.stickpeople.com/projects/python/win-psycopg/psycopg2-2.2 (py)pokereval v138 ... http://sourceforge.net/projects/fpdb/files/fpdb/pypoker-eval-win32/pokereval-138.win32.exe/download (Note: There are no official windows builds, this installer is built from source. A walkthrough is in the same directory as this walkthrough. + 1.2/ MySQL Install the following file: @@ -67,6 +68,26 @@ with this line: 1.3.4/ Save and exit +1.4/ Patch py2exe to stop popup runtime error message + +see http://www.py2exe.org/index.cgi/StderrLog for technical info. + +1.4.1/ + +dos> write C:\Python26\Lib\site-packages\py2exe\boot_common.py + +replace: + atexit.register(alert, 0, + "See the logfile '%s' for details" % fname, + "Errors occurred") +with: + #atexit.register(alert, 0, + # "See the logfile '%s' for details" % fname, + # "Errors occurred") + +1.4.2/ save and exit + + Step 2 Setup GTK ----------------- From f12bc7bfaa399880627f240583d2098d2b99894a Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 7 Jan 2011 13:58:15 +0800 Subject: [PATCH 04/32] Add printdata capability for Gametype info. --- pyfpdb/Database.py | 16 ++++++++++++---- pyfpdb/Hand.py | 4 ++-- pyfpdb/fpdb_import.py | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 8ed0e3bc..e3a8e82e 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -2169,7 +2169,7 @@ class Database: dup = True return dup - def getGameTypeId(self, siteid, game): + def getGameTypeId(self, siteid, game, printdata = False): c = self.get_cursor() #FIXME: Fixed for NL at the moment c.execute(self.sql.query['getGametypeNL'], (siteid, game['type'], game['category'], game['limitType'], game['currency'], @@ -2181,14 +2181,22 @@ class Database: hilo = "s" elif game['category'] in ['razz','27_3draw','badugi', '27_1draw']: hilo = "l" + #FIXME: recognise currency + #TODO: this wont work for non-standard structures tmp = self.insertGameTypes( (siteid, game['currency'], game['type'], game['base'], game['category'], game['limitType'], hilo, int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100), - int(Decimal(game['bb'])*100), int(Decimal(game['bb'])*200)) ) #TODO: this wont work for non-standard structures - #FIXME: recognise currency + int(Decimal(game['bb'])*100), int(Decimal(game['bb'])*200)), printdata = printdata) return tmp[0] - def insertGameTypes(self, row): + def insertGameTypes(self, row, printdata = False): + if printdata: + print _("######## Gametype ##########") + import pprint + pp = pprint.PrettyPrinter(indent=4) + pp.pprint(row) + print _("###### End Gametype ########") + c = self.get_cursor() c.execute( self.sql.query['insertGameTypes'], row ) return [self.get_last_insert_id(c)] diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 405a56e5..0556cc9e 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -226,7 +226,7 @@ dealt whether they were seen in a 'dealt to' line self.holecards[street][player] = [open, closed] - def prepInsert(self, db): + def prepInsert(self, db, printtest = False): ##### # Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables # These functions are intended for prep insert eventually @@ -235,7 +235,7 @@ dealt whether they were seen in a 'dealt to' line self.dbid_pids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) #Gametypes - self.dbid_gt = db.getGameTypeId(self.siteId, self.gametype) + self.dbid_gt = db.getGameTypeId(self.siteId, self.gametype, printdata = printtest) if self.tourNo!=None: self.tourneyTypeId = db.createTourneyType(self) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index ba1251ac..5fb43632 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -477,7 +477,7 @@ class Importer: for hand in handlist: if hand is not None: - hand.prepInsert(self.database) + hand.prepInsert(self.database, printtest = self.settings['testData']) try: hand.insert(self.database, printtest = self.settings['testData']) except Exceptions.FpdbHandDuplicate: From 503c00e1b03417fd8f53d7b3c139f1ebfbfab672 Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 7 Jan 2011 16:08:31 +0800 Subject: [PATCH 05/32] Remove unused minPrint option --- pyfpdb/GuiAutoImport.py | 4 ---- pyfpdb/GuiBulkImport.py | 4 ---- pyfpdb/fpdb_import.py | 4 ---- 3 files changed, 12 deletions(-) diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index 03bec89d..70a12215 100755 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -59,7 +59,6 @@ class GuiAutoImport (threading.Thread): self.importer = fpdb_import.Importer(self, self.settings, self.config, self.sql) self.importer.setCallHud(True) - self.importer.setMinPrint(settings['minPrint']) self.importer.setQuiet(False) self.importer.setFailOnError(False) self.importer.setHandCount(0) @@ -338,14 +337,11 @@ if __name__== "__main__": parser = OptionParser() parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True, help="don't start gui") - parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int", - help=_("How often to print a one-line status report (0 (default) means never)")) (options, argv) = parser.parse_args() config = Configuration.Config() settings = {} - settings['minPrint'] = options.minPrint if os.name == 'nt': settings['os'] = 'windows' else: settings['os'] = 'linuxmac' diff --git a/pyfpdb/GuiBulkImport.py b/pyfpdb/GuiBulkImport.py index 5ea0a24b..ba6c1123 100755 --- a/pyfpdb/GuiBulkImport.py +++ b/pyfpdb/GuiBulkImport.py @@ -68,7 +68,6 @@ class GuiBulkImport(): # get the import settings from the gui and save in the importer self.importer.setHandCount(int(self.spin_hands.get_text())) - self.importer.setMinPrint(int(self.spin_hands.get_text())) self.importer.setQuiet(self.chk_st_st.get_active()) self.importer.setFailOnError(self.chk_fail.get_active()) self.importer.setThreads(int(self.spin_threads.get_text())) @@ -344,8 +343,6 @@ def main(argv=None): help=_("Conversion filter (*Full Tilt Poker, PokerStars, Everleaf, Absolute)")) parser.add_option("-x", "--failOnError", action="store_true", default=False, help=_("If this option is passed it quits when it encounters any error")) - parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int", - help=_("How often to print a one-line status report (0 (default) means never)")) parser.add_option("-u", "--usage", action="store_true", dest="usage", default=False, help=_("Print some useful one liners")) parser.add_option("-s", "--starsarchive", action="store_true", dest="starsArchive", default=False, @@ -369,7 +366,6 @@ def main(argv=None): config = Configuration.Config() settings = {} - settings['minPrint'] = options.minPrint if os.name == 'nt': settings['os'] = 'windows' else: settings['os'] = 'linuxmac' diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 5fb43632..ee43ce8e 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -86,7 +86,6 @@ class Importer: self.cacheSessions = self.config.get_import_parameters().get("cacheSessions") # CONFIGURATION OPTIONS - self.settings.setdefault("minPrint", 30) self.settings.setdefault("handCount", 0) #self.settings.setdefault("allowHudcacheRebuild", True) # NOT USED NOW #self.settings.setdefault("forceThreads", 2) # NOT USED NOW @@ -115,9 +114,6 @@ class Importer: def setCacheSessions(self, value): self.cacheSessions = value - def setMinPrint(self, value): - self.settings['minPrint'] = int(value) - def setHandCount(self, value): self.settings['handCount'] = int(value) From 36e2ef3f35613ec19197bb99fecef0a11cf94ffc Mon Sep 17 00:00:00 2001 From: gimick Date: Thu, 30 Dec 2010 23:49:39 +0000 Subject: [PATCH 06/32] rushnotes: initial commit (for testing and eval only) --- pyfpdb/RushNotesAux.py | 216 +++++++++++++++++++++++++++++++++++++ pyfpdb/RushNotesMerge.py | 122 +++++++++++++++++++++ pyfpdb/RushNotesReadMe.txt | 166 ++++++++++++++++++++++++++++ 3 files changed, 504 insertions(+) create mode 100644 pyfpdb/RushNotesAux.py create mode 100755 pyfpdb/RushNotesMerge.py create mode 100644 pyfpdb/RushNotesReadMe.txt diff --git a/pyfpdb/RushNotesAux.py b/pyfpdb/RushNotesAux.py new file mode 100644 index 00000000..bb1ff506 --- /dev/null +++ b/pyfpdb/RushNotesAux.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""RushNotesAux.py + + EXPERIMENTAL - USE WITH CARE + +Auxilliary process to push HUD data into the FullTilt player notes XML +This will allow a rudimentary "HUD" in rush games + +The existing notes file will be altered by this function +""" +# Copyright 2010, "Gimick" of the FPDB project fpdb.sourceforge.net +# +#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. + +######################################################################## + +##########for each hand processed, attempts to update hero.xml player notes for FullTilt +##########based upon the AW howto notes written by Ray E. Barker (nutomatic) at fpdb.sourceforge.net + +#to do +### think about seeding +### multiple huds firing at the same xml +### same player / two levels / only one xml +### http://www.faqs.org/docs/diveintopython/kgp_search.html + +#debugmode will write logfiles for the __init__ and update_data methods +debugmode = False + +# Standard Library modules +import os +from xml.dom import minidom +from datetime import datetime +from time import * + +# FreePokerDatabase modules +from Mucked import Aux_Window +from Mucked import Seat_Window +from Mucked import Aux_Seats +import Stats + +class RushNotes(Aux_Window): + + def __init__(self, hud, config, params): + + self.hud = hud + self.config = config + + # + # following line makes all the site params magically available (thanks Ray!) + # + site_params_dict = self.hud.config.get_site_parameters(self.hud.site) + + heroname = site_params_dict['screen_name'] + sitename = site_params_dict['site_name'] + notepath = site_params_dict['site_path'] # this is a temporary hijack of site-path + notepath = r"/home/steve/.wine/drive_c/Program Files/Full Tilt Poker/" + self.heroid = self.hud.db_connection.get_player_id(self.config, sitename, heroname) + self.notefile = notepath + "/" + heroname + ".xml" + + # + # read in existing notefile and backup with date/time in name + # todo change to not use dom + # + now = datetime.now() + notefilebackup = self.notefile + ".backup." + now.strftime("%Y%m%d%H%M%S") + xmlnotefile = minidom.parse(self.notefile) + outputfile = open(notefilebackup, 'w') + xmlnotefile.writexml(outputfile) + outputfile.close() + xmlnotefile.unlink + + # Create a fresh queue file with skeleton XML + # + self.queuefile = self.notefile + ".queue" + queuedom = minidom.Document() + # Create the minidom document + +# Create the base element + pld=queuedom.createElement("PLAYERDATA") + queuedom.appendChild(pld) + + nts=queuedom.createElement("NOTES") + pld.appendChild(nts) + + nte = queuedom.createElement("NOTE") + nte = queuedom.createTextNode("\n") + nts.insertBefore(nte,None) + + outputfile = open(self.queuefile, 'w') + queuedom.writexml(outputfile) + outputfile.close() + queuedom.unlink + + if (debugmode): + #initialise logfiles + debugfile=open("~Rushdebug.init", "w") + debugfile.write("conf="+str(config)+"\n") + debugfile.write("spdi="+str(site_params_dict)+"\n") + debugfile.write("para="+str(params)+"\n") + debugfile.write("hero="+heroname+" "+str(self.heroid)+"\n") + debugfile.write("back="+notefilebackup+"\n") + debugfile.write("queu="+self.queuefile+"\n") + debugfile.close() + + open("~Rushdebug.data", "w").close() + + + def update_data(self, new_hand_id, db_connection): + #this method called once for every hand processed + # self.hud.stat_dict contains the stats information for this hand + + + + if (debugmode): + debugfile=open("~Rushdebug.data", "a") + debugfile.write(new_hand_id+"\n") + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+ " update_data begins"+ "\n") + debugfile.write("hero="+str(self.heroid)+"\n") + #debugfile.write(str(self.hud.stat_dict)+"\n") + debugfile.write(self.hud.table.name+"\n") + debugfile.write(str(self.hud.stat_dict.keys())+"\n") + + if self.hud.table.name not in {"Mach 10", "Lightning", "Celerity", "Flash", "Zoom"} + return + # + # Grab a list of player id's + # + handplayers = self.hud.stat_dict.keys() + + # + # build a dictionary of stats text for each player in the hand (excluding the hero) + # xmlqueuedict contains {playername : stats text} + # + xmlqueuedict = {} + for playerid in handplayers: + # ignore hero, no notes available for hero at Full Tilt + if playerid == self.heroid: continue + + playername=unicode(str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'playername')[1])) + # Use index[3] which is a short description + n=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'n')[3] + " ") + vpip=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'vpip')[3] + " ") + pfr=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'pfr')[3] + " ") + three_B=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'three_B')[3] + " ") + cbet=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'cbet')[3] + " ") + steal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'steal')[3] + " ") + ffreq1=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'ffreq1')[3] + " ") + agg_freq=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'agg_freq')[3] + " ") + BBper100=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'BBper100')[3] + " ") + + xmlqueuedict[playername] = "~fpdb~" + n + vpip + pfr + three_B + cbet + steal + ffreq1 + agg_freq + BBper100 + "~ends~" + + if (debugmode): + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+" villain data has been processed" + "\n") + debugfile.write(str(xmlqueuedict)+"\n") + + # + # delaying processing of xml until now. Grab current queuefile contents and + # read each existing NOTE element in turn, if matched to a player in xmlqueuedict + # update their text in the xml and delete the dictionary item + # + xmlnotefile = minidom.parse(self.queuefile) + notelist = xmlnotefile.getElementsByTagName('NOTE') + + for noteentry in notelist: #for each note in turn + noteplayer = noteentry.getAttribute("PlayerId") #extract the playername from xml + if noteplayer in xmlqueuedict: # does that player exist in the queue? + noteentry.setAttribute("Text",xmlqueuedict[noteplayer]) + del xmlqueuedict[noteplayer] #remove from list, does not need to be added later on + + # + #create entries for new players (those remaining in the dictionary) + # + if len(xmlqueuedict) > 0: + playerdata=xmlnotefile.lastChild #move to the PLAYERDATA node (assume last one in the list) + notesnode=playerdata.childNodes[0] #Find NOTES node + + for newplayer in xmlqueuedict: + newentry = xmlnotefile.createElement("NOTE") + newentry.setAttribute("PlayerId", newplayer) + newentry.setAttribute("Text", xmlqueuedict[newplayer]) + notesnode.insertBefore(newentry,None) + newentry = xmlnotefile.createTextNode("\n") + notesnode.insertBefore(newentry,None) + + if (debugmode): + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+" xml pre-processing complete"+ "\n") + + # + # OverWrite existing xml file with updated DOM and cleanup + # + updatednotes = open(self.queuefile, 'w') + xmlnotefile.writexml(updatednotes) + updatednotes.close() + + xmlnotefile.unlink + + if (debugmode): + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n") + debugfile.close() diff --git a/pyfpdb/RushNotesMerge.py b/pyfpdb/RushNotesMerge.py new file mode 100755 index 00000000..ec8896d1 --- /dev/null +++ b/pyfpdb/RushNotesMerge.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""RushNotesMerge.py + + EXPERIMENTAL - USE WITH CARE + +Merge .queue file with hero's note to generate fresh .merge file + +normal usage +$> ./pyfpdb/RushNotesMerge.py "/home/steve/.wine/drive_c/Program Files/Full Tilt Poker/heroname.xml" + +The generated file can then replace heroname.xml (if all is well). + + +""" +# Copyright 2010, "Gimick" of the FPDB project fpdb.sourceforge.net +# +#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. + +######################################################################## + + +# Standard Library modules +import os +import sys +from xml.dom import minidom + +statqueue=0 +statupdated=0 +statadded=0 + +def cleannote(textin): + if textin.find("~fpdb~") == -1: return textin + if textin.find("~ends~") == -1: return textin + if textin.find("~fpdb~") > textin.find("~ends~"): return textin + return textin[0:textin.find("~fpdb~")] + textin[textin.find("~ends~")+6:] +# get out now if parameter not passed +try: + sys.argv[1] <> "" +except: + print "A parameter is required, quitting now" + quit() + +if not os.path.isfile(sys.argv[1]): + print "Hero notes file not found, quitting" + quit() + +if not os.path.isfile((sys.argv[1]+".queue")): + print "Nothing queued, quitting" + quit() + +print "reading from: ", sys.argv[1] +print "merging with: ", sys.argv[1]+".queue" + +#read queue and turn into a dict +queuedict = {} +xmlqueue = minidom.parse(sys.argv[1]+".queue") +notelist = xmlqueue.getElementsByTagName('NOTE') + +for noteentry in notelist: + noteplayer = noteentry.getAttribute("PlayerId") + notetext = noteentry.getAttribute("Text") + queuedict[noteplayer] = notetext + statqueue = statqueue + 1 + +#read existing player note file + +xmlnotefile = minidom.parse(sys.argv[1]) +notelist = xmlnotefile.getElementsByTagName('NOTE') + +# +#for existing players, empty out existing fpdbtext and refill +# +for noteentry in notelist: + noteplayer = noteentry.getAttribute("PlayerId") + if noteplayer in queuedict: + existingnote = noteentry.getAttribute("Text") + newnote=cleannote(existingnote) + newnote = newnote + queuedict[noteplayer] + noteentry.setAttribute("Text",newnote) + statupdated = statupdated + 1 + del queuedict[noteplayer] + +# +#create entries for new players (those remaining in the dictionary) +# +if len(queuedict) > 0: + playerdata=xmlnotefile.lastChild #move to the PLAYERDATA node (assume last one in the list) + notesnode=playerdata.childNodes[1] #Find NOTES node + +for newplayer in queuedict: + newentry = xmlnotefile.createElement("NOTE") + newentry.setAttribute("PlayerId", newplayer) + newentry.setAttribute("Text", queuedict[newplayer]) + notesnode.insertBefore(newentry,None) + newentry = xmlnotefile.createTextNode("\n") + notesnode.insertBefore(newentry,None) + statadded=statadded+1 + +#print xmlnotefile.toprettyxml() + +mergednotes = open(sys.argv[1]+".merged", 'w') +xmlnotefile.writexml(mergednotes) +mergednotes.close() + +xmlnotefile.unlink + +print "new file has been written to: ", sys.argv[1]+".merged" +print "number in queue: ", statqueue +print "existing players updated: ", statupdated +print "new players added: ", statadded diff --git a/pyfpdb/RushNotesReadMe.txt b/pyfpdb/RushNotesReadMe.txt new file mode 100644 index 00000000..595b5adb --- /dev/null +++ b/pyfpdb/RushNotesReadMe.txt @@ -0,0 +1,166 @@ +aux to write fpdb data to player notes on Full Tilt +--------------------------------------------------- + +by Gimick 30th Dec 2010 + +RushNotesAux - auxillary processed attached to the full tilt hud + builds up fpdb notes "queue" for each villain met while the autoimport is running + uses HUD aggregation stats to do this + +RushNotesMerge - stand alone process to merge the existing ftp notes, together with queue + produced by Aux. + the output file can then be renamed to become the new ftp notes file + +Important info: +The Merge process can only be run when ftp client is shutdown - otherwise ftp overwrites the xml on exit. + +Restarting the autoimport will empty the notes"queue" so avoid restarting autoimport until the previous +notes "queue" has been merged. + +Existing ftp notes _SHOULD_ be preserved, but this isn't guaranteed, you have been warned +Existing colour codings should be preserved, this process should not change colourcodings. + +Copies of the live ftp notes file are preserved everytime RushNotesAux is started, just in case. + +The AW is hard-coded with just the table names of Micro Rush Poker, and should ignore all other hands. + +Getting started: +--------------- + +1. Set the Hero aggregation to alltime. hero_stat_range="A" + This overcomes a sqlite "bug" which has nothing to do with auxillary windows - not doing this + will slow processing down to about 1 hand per minute. + +2. Set the site_path to be the folder containing the FTP notes xml file +(on wine this is normally site_path="/home/blah/.wine/Program Files/Full Tilt Poker/") + + +Wire-up the aux process: +----------------------- + + + + +or whatever works for you. + +Start Autoimport, and rearrange the on-screen stats out of the way + (killing the HUD kills the AW updates) + +Play some poker + +Stop the autoimport +Exit the Full tilt poker client (ensure it has fully stopped with ps -A) + +execute the following: + +./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myname.xml" + +A revised notes file (blah.merge) should automagically appear in the full tilt root directory. +If you are happy with it, replace the existing (myname.xml file) + + +Summary +------ + +This is very rough and ready, but it does what I set-out to achieve. + +All feedback welcome, and if this is useful as a basis for general notes processing, then thats great. + +As I find bugs and make improvements, I will push to the git branch. + + +Much more information below: +---------------------------- + +Background +---------- + +When playing rush poker, some sort of rudimentary HUD would answer simple questions +like "is this allin overbet being made by a nit, or a maniac". Although some +notes may have been made previously, some statistics would help to backup the decision. + +Currently fpdb cannot support rush because the HUD is always 1 hand or more +behind the current action. + +The only way to do this at the moment is to switch to GuiPlayerStats and make a quick +enquiry by player name. However, this effectively times you out of all other +action if multitabling. + +Full Tilt situation +------------------- + +Full Tilt notes are stored in xml format ("hero.xml"). Previously these could +be updated while the game was in progress, however, FullTilt now cache the +notes and write them out when the application exits. This makes it impossible +to use the notes as a real-time HUD, and therefore real-time huds are now +forced to screen-scrape or poke around in the client memory. + +Accepting this a limitation, this implementation updates the notes only once +the FullTilt client has been closed. Obviously, the villain HUD stats are only +as at the end of the last session, however, it is hoped this is significantly +better than having nothing at all. As the hero's hand history increases, the +notes should progressively mature in accuracy. + +Preamble +-------- + +Note that this implementation was written purely to be "good enough" to work +for the author, and is not intended as package or production quality. It +is contributed as a starting point for others, or for experimental use. + +Thanks to Ray Barker who gave a great deal of help throughout. + + +The implementation +------------------- + +RushNotesAux is an fpdb auxilliary process, and is called for every hand +processed by autoimport. Each villain has a note prepared based on the current +fpdb data, and this note (in XML format) is stored in a queue file. + +Auxilliary windows were chosen because +a) the author has limited fpdb and programming skill +b) the auxillary windows handler is well documented and supported +c) any code created has access to the full range of stats with little or no extra work +d) runs within the HUD, so the aggregation parameters are already available + + +Limitations +----------- + +The notes are only regenerated if a hand is played against the villain. The +process does not "bulk load" notes based upon all the player stats in FPDB. + +It is hoped that due to the relatively large hand volume and relatively small + player pools, this limitation will be largely overcome after a few sessions +although there will obviously be a number of players with no fpdb note. + +The aggregation parameters used for the notes are based upon the HUD parameters. + +Stopping and starting the HUD will erase the previously created notes holding file. + +The HUD must run, so the individual player popups need to be manually relocated. + +Although hard-coded for micro RUSH tablenames, the auxilliary window will +probably happily update notes of all cash and tournament players. + +Process overview +---------------- + +1/ The HUD process is started. +1.1/ when the first hand is received, h fresh holding file is created, and +a copy of the current live xml note file is created as a security backup. +2/ For every hand played, the auxillary window is called +3/ Based upon the players in the hand, fpdb will be interrogated +and key stats are formatted in xml-style and written out to a holding file. +4/ At the end of the session, the HUD is stopped and the poker client closed + +5/ The user can then review the contents of the holding file. +6/ A process is begun to "merge" the holding file into the existing player notes +7/ A new "merged" file is created. The process attempts to preserve any +existing notes, but this cannot be guaranteed. +8/ The user can review this merged file, and if they are happy, +they replace the existing note file. +9/ Note that this process never updates the live notes file in situ, but +there is a risk that something goes wrong, and that existing notes could be destroyed. + From afaaee1fcf69c82982f97b8aad41549f8aa0d729 Mon Sep 17 00:00:00 2001 From: gimick Date: Fri, 31 Dec 2010 00:19:52 +0000 Subject: [PATCH 07/32] rushnotes: fix typo, switch off debug mode --- pyfpdb/RushNotesAux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/RushNotesAux.py b/pyfpdb/RushNotesAux.py index bb1ff506..1a87fd78 100644 --- a/pyfpdb/RushNotesAux.py +++ b/pyfpdb/RushNotesAux.py @@ -133,7 +133,7 @@ class RushNotes(Aux_Window): debugfile.write(self.hud.table.name+"\n") debugfile.write(str(self.hud.stat_dict.keys())+"\n") - if self.hud.table.name not in {"Mach 10", "Lightning", "Celerity", "Flash", "Zoom"} + if self.hud.table.name not in ("Mach 10", "Lightning", "Celerity", "Flash", "Zoom"): return # # Grab a list of player id's From ba3883d0b75ef4aeb8bf1b92f94ee14a55c25b4f Mon Sep 17 00:00:00 2001 From: gimick Date: Sat, 1 Jan 2011 18:53:53 +0000 Subject: [PATCH 08/32] rushnotes: tweaks + overcome "lf" bug in minidom + place HUD on multiline --- pyfpdb/RushNotesAux.py | 66 +++++++++++++++++++++++++++++++++++++--- pyfpdb/RushNotesMerge.py | 42 +++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/pyfpdb/RushNotesAux.py b/pyfpdb/RushNotesAux.py index 1a87fd78..28e3aaaa 100644 --- a/pyfpdb/RushNotesAux.py +++ b/pyfpdb/RushNotesAux.py @@ -36,10 +36,11 @@ The existing notes file will be altered by this function ### http://www.faqs.org/docs/diveintopython/kgp_search.html #debugmode will write logfiles for the __init__ and update_data methods -debugmode = False +debugmode = True # Standard Library modules import os +import sys from xml.dom import minidom from datetime import datetime from time import * @@ -50,6 +51,48 @@ from Mucked import Seat_Window from Mucked import Aux_Seats import Stats +# +# overload minidom methods to fix bug where \n is parsed as " ". +# described here: http://bugs.python.org/issue7139 +# + +def _write_data(writer, data, isAttrib=False): + "Writes datachars to writer." + if isAttrib: + data = data.replace("\r", " ").replace("\n", " ") + data = data.replace("\t", " ") + writer.write(data) +minidom._write_data = _write_data + +def writexml(self, writer, indent="", addindent="", newl=""): + # indent = current indentation + # addindent = indentation to add to higher levels + # newl = newline string + writer.write(indent+"<" + self.tagName) + + attrs = self._get_attributes() + a_names = attrs.keys() + a_names.sort() + + for a_name in a_names: + writer.write(" %s=\"" % a_name) + _write_data(writer, attrs[a_name].value, isAttrib=True) + writer.write("\"") + if self.childNodes: + writer.write(">%s"%(newl)) + for node in self.childNodes: + node.writexml(writer,indent+addindent,addindent,newl) + writer.write("%s%s" % (indent,self.tagName,newl)) + else: + writer.write("/>%s"%(newl)) +# For an introduction to overriding instance methods, see +# http://irrepupavel.com/documents/python/instancemethod/ +instancemethod = type(minidom.Element.writexml) +minidom.Element.writexml = instancemethod( + writexml, None, minidom.Element) + + + class RushNotes(Aux_Window): def __init__(self, hud, config, params): @@ -65,9 +108,14 @@ class RushNotes(Aux_Window): heroname = site_params_dict['screen_name'] sitename = site_params_dict['site_name'] notepath = site_params_dict['site_path'] # this is a temporary hijack of site-path - notepath = r"/home/steve/.wine/drive_c/Program Files/Full Tilt Poker/" self.heroid = self.hud.db_connection.get_player_id(self.config, sitename, heroname) self.notefile = notepath + "/" + heroname + ".xml" + + if not os.path.isfile(self.notefile): + self.active = False + return + else: + self.active = True # # read in existing notefile and backup with date/time in name @@ -121,7 +169,8 @@ class RushNotes(Aux_Window): #this method called once for every hand processed # self.hud.stat_dict contains the stats information for this hand - + if not self.active: + return if (debugmode): debugfile=open("~Rushdebug.data", "a") @@ -156,13 +205,15 @@ class RushNotes(Aux_Window): pfr=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'pfr')[3] + " ") three_B=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'three_B')[3] + " ") cbet=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'cbet')[3] + " ") + fbbsteal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'f_BB_steal')[3] + " ") + steal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'steal')[3] + " ") ffreq1=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'ffreq1')[3] + " ") agg_freq=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'agg_freq')[3] + " ") BBper100=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'BBper100')[3] + " ") - xmlqueuedict[playername] = "~fpdb~" + n + vpip + pfr + three_B + cbet + steal + ffreq1 + agg_freq + BBper100 + "~ends~" - + xmlqueuedict[playername] = "~fpdb~" + n + vpip + pfr + three_B + fbbsteal + "\n" + steal + cbet + ffreq1 + "\n" + agg_freq + BBper100 + "~ends~" + if (debugmode): now = datetime.now() debugfile.write(now.strftime("%Y%m%d%H%M%S")+" villain data has been processed" + "\n") @@ -214,3 +265,8 @@ class RushNotes(Aux_Window): now = datetime.now() debugfile.write(now.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n") debugfile.close() + + + + + diff --git a/pyfpdb/RushNotesMerge.py b/pyfpdb/RushNotesMerge.py index ec8896d1..8d46c3fa 100755 --- a/pyfpdb/RushNotesMerge.py +++ b/pyfpdb/RushNotesMerge.py @@ -36,6 +36,48 @@ import os import sys from xml.dom import minidom +# +# overload minidom methods to fix bug where \n is parsed as " ". +# described here: http://bugs.python.org/issue7139 +# + +def _write_data(writer, data, isAttrib=False): + "Writes datachars to writer." + if isAttrib: + data = data.replace("\r", " ").replace("\n", " ") + data = data.replace("\t", " ") + writer.write(data) +minidom._write_data = _write_data + +def writexml(self, writer, indent="", addindent="", newl=""): + # indent = current indentation + # addindent = indentation to add to higher levels + # newl = newline string + writer.write(indent+"<" + self.tagName) + + attrs = self._get_attributes() + a_names = attrs.keys() + a_names.sort() + + for a_name in a_names: + writer.write(" %s=\"" % a_name) + _write_data(writer, attrs[a_name].value, isAttrib=True) + writer.write("\"") + if self.childNodes: + writer.write(">%s"%(newl)) + for node in self.childNodes: + node.writexml(writer,indent+addindent,addindent,newl) + writer.write("%s%s" % (indent,self.tagName,newl)) + else: + writer.write("/>%s"%(newl)) +# For an introduction to overriding instance methods, see +# http://irrepupavel.com/documents/python/instancemethod/ +instancemethod = type(minidom.Element.writexml) +minidom.Element.writexml = instancemethod( + writexml, None, minidom.Element) + + + statqueue=0 statupdated=0 statadded=0 From 96275f9f1e0685019ef77e8fdfffde72c7bfd478 Mon Sep 17 00:00:00 2001 From: gimick Date: Wed, 5 Jan 2011 22:35:42 +0000 Subject: [PATCH 09/32] rushnotes: more tweaks - add LF's and brackets for negative BB100 --- pyfpdb/RushNotesAux.py | 11 ++++++++--- pyfpdb/RushNotesMerge.py | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/pyfpdb/RushNotesAux.py b/pyfpdb/RushNotesAux.py index 28e3aaaa..3a445a0f 100644 --- a/pyfpdb/RushNotesAux.py +++ b/pyfpdb/RushNotesAux.py @@ -36,7 +36,7 @@ The existing notes file will be altered by this function ### http://www.faqs.org/docs/diveintopython/kgp_search.html #debugmode will write logfiles for the __init__ and update_data methods -debugmode = True +debugmode = False # Standard Library modules import os @@ -210,9 +210,14 @@ class RushNotes(Aux_Window): steal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'steal')[3] + " ") ffreq1=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'ffreq1')[3] + " ") agg_freq=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'agg_freq')[3] + " ") - BBper100=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'BBper100')[3] + " ") + BBper100=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'BBper100')[3]) + if BBper100[6] == "-": BBper100=BBper100[0:6] + "(" + BBper100[7:] + ")" - xmlqueuedict[playername] = "~fpdb~" + n + vpip + pfr + three_B + fbbsteal + "\n" + steal + cbet + ffreq1 + "\n" + agg_freq + BBper100 + "~ends~" + xmlqueuedict[playername] = ("~fpdb~" + "\n" + + n + vpip + pfr + three_B + fbbsteal + "\n" + + steal + cbet + ffreq1 + "\n" + + agg_freq + BBper100 + "\n" + + "~ends~") if (debugmode): now = datetime.now() diff --git a/pyfpdb/RushNotesMerge.py b/pyfpdb/RushNotesMerge.py index 8d46c3fa..f22b67ab 100755 --- a/pyfpdb/RushNotesMerge.py +++ b/pyfpdb/RushNotesMerge.py @@ -102,8 +102,15 @@ if not os.path.isfile((sys.argv[1]+".queue")): print "Nothing queued, quitting" quit() -print "reading from: ", sys.argv[1] -print "merging with: ", sys.argv[1]+".queue" +print "***************************************************************" +print "IMPORTANT: *** Before running this merge: ***" +print "Closedown the FullTiltClient and wait for it to completely stop" +print "If FullTiltClient was running, run the merge again once it" +print "has stopped completely" +print "***************************************************************" +print +print "read from: ", sys.argv[1] +print "merge with: ", sys.argv[1]+".queue" #read queue and turn into a dict queuedict = {} @@ -158,7 +165,13 @@ mergednotes.close() xmlnotefile.unlink -print "new file has been written to: ", sys.argv[1]+".merged" +print "Merged file has been written to: ", sys.argv[1]+".merged" +print "" print "number in queue: ", statqueue print "existing players updated: ", statupdated print "new players added: ", statadded +print "\n" +print "Use a viewer to check the contents of the merge file." +print "If you are happy, carry out the following steps:" +print "1 Rename or delete the existing notes file (normally .xml" +print "2 Rename the .merged file to become the new notes file" From ae7f983591008d339844dcea1e926eb23bd51eed Mon Sep 17 00:00:00 2001 From: gimick Date: Sat, 8 Jan 2011 23:28:41 +0000 Subject: [PATCH 10/32] rushnotes: major update, show villain ranges in HUD + other tweaks --- pyfpdb/RushNotesAux.py | 128 ++++++++++++++++++++++++++----------- pyfpdb/RushNotesMerge.py | 12 +++- pyfpdb/RushNotesReadMe.txt | 60 ++++++++++++----- 3 files changed, 145 insertions(+), 55 deletions(-) diff --git a/pyfpdb/RushNotesAux.py b/pyfpdb/RushNotesAux.py index 3a445a0f..312de6d5 100644 --- a/pyfpdb/RushNotesAux.py +++ b/pyfpdb/RushNotesAux.py @@ -26,16 +26,14 @@ The existing notes file will be altered by this function ######################################################################## -##########for each hand processed, attempts to update hero.xml player notes for FullTilt +##########for each hand processed, attempts to create update for player notes in FullTilt ##########based upon the AW howto notes written by Ray E. Barker (nutomatic) at fpdb.sourceforge.net +##########Huge thanks to Ray for his guidance and encouragement to create this !! -#to do -### think about seeding -### multiple huds firing at the same xml -### same player / two levels / only one xml -### http://www.faqs.org/docs/diveintopython/kgp_search.html - +# #debugmode will write logfiles for the __init__ and update_data methods +# writes into ./pyfpdb/~Rushdebug.* +# debugmode = False # Standard Library modules @@ -50,9 +48,10 @@ from Mucked import Aux_Window from Mucked import Seat_Window from Mucked import Aux_Seats import Stats +import Card # -# overload minidom methods to fix bug where \n is parsed as " ". +# overload minidom methods to fix bug where \n is parsed as " ". # described here: http://bugs.python.org/issue7139 # @@ -110,7 +109,8 @@ class RushNotes(Aux_Window): notepath = site_params_dict['site_path'] # this is a temporary hijack of site-path self.heroid = self.hud.db_connection.get_player_id(self.config, sitename, heroname) self.notefile = notepath + "/" + heroname + ".xml" - + self.rushtables = ("Mach 10", "Lightning", "Celerity", "Flash", "Zoom") + if not os.path.isfile(self.notefile): self.active = False return @@ -129,28 +129,27 @@ class RushNotes(Aux_Window): outputfile.close() xmlnotefile.unlink + # # Create a fresh queue file with skeleton XML - # + # self.queuefile = self.notefile + ".queue" - queuedom = minidom.Document() - # Create the minidom document + queuedom = minidom.Document() -# Create the base element pld=queuedom.createElement("PLAYERDATA") queuedom.appendChild(pld) nts=queuedom.createElement("NOTES") pld.appendChild(nts) - + nte = queuedom.createElement("NOTE") nte = queuedom.createTextNode("\n") - nts.insertBefore(nte,None) - + nts.insertBefore(nte,None) + outputfile = open(self.queuefile, 'w') queuedom.writexml(outputfile) outputfile.close() queuedom.unlink - + if (debugmode): #initialise logfiles debugfile=open("~Rushdebug.init", "w") @@ -159,19 +158,19 @@ class RushNotes(Aux_Window): debugfile.write("para="+str(params)+"\n") debugfile.write("hero="+heroname+" "+str(self.heroid)+"\n") debugfile.write("back="+notefilebackup+"\n") - debugfile.write("queu="+self.queuefile+"\n") + debugfile.write("queu="+self.queuefile+"\n") debugfile.close() - + open("~Rushdebug.data", "w").close() - + def update_data(self, new_hand_id, db_connection): #this method called once for every hand processed # self.hud.stat_dict contains the stats information for this hand - + if not self.active: return - + if (debugmode): debugfile=open("~Rushdebug.data", "a") debugfile.write(new_hand_id+"\n") @@ -179,10 +178,11 @@ class RushNotes(Aux_Window): debugfile.write(now.strftime("%Y%m%d%H%M%S")+ " update_data begins"+ "\n") debugfile.write("hero="+str(self.heroid)+"\n") #debugfile.write(str(self.hud.stat_dict)+"\n") - debugfile.write(self.hud.table.name+"\n") - debugfile.write(str(self.hud.stat_dict.keys())+"\n") + debugfile.write("table="+self.hud.table.name+"\n") + debugfile.write("players="+str(self.hud.stat_dict.keys())+"\n") + debugfile.write("db="+str(db_connection)+"\n") - if self.hud.table.name not in ("Mach 10", "Lightning", "Celerity", "Flash", "Zoom"): + if self.hud.table.name not in self.rushtables: return # # Grab a list of player id's @@ -212,18 +212,74 @@ class RushNotes(Aux_Window): agg_freq=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'agg_freq')[3] + " ") BBper100=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'BBper100')[3]) if BBper100[6] == "-": BBper100=BBper100[0:6] + "(" + BBper100[7:] + ")" - - xmlqueuedict[playername] = ("~fpdb~" + "\n" + - n + vpip + pfr + three_B + fbbsteal + "\n" + - steal + cbet + ffreq1 + "\n" + - agg_freq + BBper100 + "\n" + + + + # + # grab villain known starting hands + # only those where they VPIP'd, so limp in the BB will not be shown + # sort by hand strength. Output will show position too, + # so KK.1 is KK from late posn etc. + # ignore non-rush hands (check against known rushtablenames) + # cards decoding is hard-coded for holdem, so that's tuff atm + # three categories of known hands are shown: + # agression preflop hands + # non-aggression preflop hands + # bigblind called to defend hands + # + # This isn't perfect, but it isn't too bad a starting point + # + + PFcall="PFcall" + PFaggr="PFaggr" + PFdefend="PFdefend" + + c = db_connection.get_cursor() + c.execute(("SELECT handId, position, startCards, street0Aggr, tableName " + + "FROM hands, handsPlayers " + + "WHERE handsplayers.handId = hands.id " + + "AND street0VPI = 1 " + + "AND startCards > 0 " + + "AND playerId = %d " + + "ORDER BY startCards DESC " + + ";") + % int(playerid)) + + for (qid, qposition, qstartcards, qstreet0Aggr, qtablename) in c.fetchall(): + if (debugmode): + debugfile.write("pid, hid, pos, cards, aggr, table player"+ + str(playerid)+"/"+str(qid)+"/"+str(qposition)+"/"+ + str(qstartcards)+"/"+str(qstreet0Aggr)+"/"+ + str(qtablename)+"/"+str(playername)+ + "\n") + + humancards = Card.decodeStartHandValue("holdem", qstartcards) + + if qtablename not in self.rushtables: + pass + elif qposition == "B" and qstreet0Aggr == False: + PFdefend=PFdefend+"/"+humancards + elif qstreet0Aggr == True: + PFaggr=PFaggr+"/"+humancards+"."+qposition + else: + PFcall=PFcall+"/"+humancards+"."+qposition + c.close + + # + # build up final text package (top/tail with ~fpdb~ ~ends~ + # for later search/replace by Merge module + # + xmlqueuedict[playername] = ("~fpdb~" + "\n" + + n + vpip + pfr + three_B + fbbsteal + "\n" + + steal + cbet + ffreq1 + "\n" + + agg_freq + BBper100 + "\n" + + PFcall+"\n"+PFaggr+"\n"+PFdefend +"\n" "~ends~") if (debugmode): now = datetime.now() debugfile.write(now.strftime("%Y%m%d%H%M%S")+" villain data has been processed" + "\n") debugfile.write(str(xmlqueuedict)+"\n") - + # # delaying processing of xml until now. Grab current queuefile contents and # read each existing NOTE element in turn, if matched to a player in xmlqueuedict @@ -244,26 +300,26 @@ class RushNotes(Aux_Window): if len(xmlqueuedict) > 0: playerdata=xmlnotefile.lastChild #move to the PLAYERDATA node (assume last one in the list) notesnode=playerdata.childNodes[0] #Find NOTES node - + for newplayer in xmlqueuedict: newentry = xmlnotefile.createElement("NOTE") newentry.setAttribute("PlayerId", newplayer) - newentry.setAttribute("Text", xmlqueuedict[newplayer]) + newentry.setAttribute("Text", xmlqueuedict[newplayer]) notesnode.insertBefore(newentry,None) newentry = xmlnotefile.createTextNode("\n") notesnode.insertBefore(newentry,None) - + if (debugmode): now = datetime.now() debugfile.write(now.strftime("%Y%m%d%H%M%S")+" xml pre-processing complete"+ "\n") - + # # OverWrite existing xml file with updated DOM and cleanup # updatednotes = open(self.queuefile, 'w') xmlnotefile.writexml(updatednotes) updatednotes.close() - + xmlnotefile.unlink if (debugmode): diff --git a/pyfpdb/RushNotesMerge.py b/pyfpdb/RushNotesMerge.py index f22b67ab..7bfec3df 100755 --- a/pyfpdb/RushNotesMerge.py +++ b/pyfpdb/RushNotesMerge.py @@ -7,7 +7,7 @@ Merge .queue file with hero's note to generate fresh .merge file normal usage -$> ./pyfpdb/RushNotesMerge.py "/home/steve/.wine/drive_c/Program Files/Full Tilt Poker/heroname.xml" +$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/heroname.xml" The generated file can then replace heroname.xml (if all is well). @@ -37,7 +37,7 @@ import sys from xml.dom import minidom # -# overload minidom methods to fix bug where \n is parsed as " ". +# overload minidom methods to fix bug where \n is parsed as " ". # described here: http://bugs.python.org/issue7139 # @@ -92,14 +92,20 @@ try: sys.argv[1] <> "" except: print "A parameter is required, quitting now" + print "normal usage is something like:" + print '$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myhero.xml"' quit() if not os.path.isfile(sys.argv[1]): print "Hero notes file not found, quitting" + print "normal usage is something like:" + print '$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myhero.xml"' quit() if not os.path.isfile((sys.argv[1]+".queue")): - print "Nothing queued, quitting" + print "Nothing found to merge, quitting" + print "Did the HUD not get started during the last session?" + print "Has the HUD been stopped and started without merging?" quit() print "***************************************************************" diff --git a/pyfpdb/RushNotesReadMe.txt b/pyfpdb/RushNotesReadMe.txt index 595b5adb..43475536 100644 --- a/pyfpdb/RushNotesReadMe.txt +++ b/pyfpdb/RushNotesReadMe.txt @@ -12,24 +12,40 @@ RushNotesMerge - stand alone process to merge the existing ftp notes, together w the output file can then be renamed to become the new ftp notes file Important info: -The Merge process can only be run when ftp client is shutdown - otherwise ftp overwrites the xml on exit. +The Merge process can only be run when ftp client is shutdown + - otherwise ftp overwrites the xml on exit. -Restarting the autoimport will empty the notes"queue" so avoid restarting autoimport until the previous -notes "queue" has been merged. +Restarting the autoimport will empty the notes"queue" so avoid restarting + autoimport until the previous notes "queue" has been merged. You will + lose all the queued notes, but these will be regenerated the next time + the villian is at your table, so it isn't the end of the world. -Existing ftp notes _SHOULD_ be preserved, but this isn't guaranteed, you have been warned -Existing colour codings should be preserved, this process should not change colourcodings. +Existing ftp notes _SHOULD_ be preserved, but this isn't guaranteed, + you have been warned! + +Existing colour codings should be preserved, + this process does not change or set colourcodings. -Copies of the live ftp notes file are preserved everytime RushNotesAux is started, just in case. +Copies of the live ftp notes file should be preserved everytime + RushNotesAux (i.e. the HUD is started) -The AW is hard-coded with just the table names of Micro Rush Poker, and should ignore all other hands. +The AW is hard-coded with just the table names of Micro Rush Poker, + and should ignore all other hands. +What might not work? +-------------------- + +This isn't tested with Windows, and probably won't work, feedback welcome. +Hasn't been tested for co-existance with other sites, feedback welcome. +Whenever FTP change their notes file format, this will all break rather spectacularly, + you have been warned! + Getting started: --------------- 1. Set the Hero aggregation to alltime. hero_stat_range="A" - This overcomes a sqlite "bug" which has nothing to do with auxillary windows - not doing this - will slow processing down to about 1 hand per minute. + This overcomes a sqlite "bug" which has nothing to do with auxillary windows + not doing this will slow processing down to about 1 hand per minute. 2. Set the site_path to be the folder containing the FTP notes xml file (on wine this is normally site_path="/home/blah/.wine/Program Files/Full Tilt Poker/") @@ -43,12 +59,17 @@ Wire-up the aux process: or whatever works for you. -Start Autoimport, and rearrange the on-screen stats out of the way - (killing the HUD kills the AW updates) Play some poker +--------------- + +Start Autoimport, and rearrange the on-screen stats out of the way + (the full HUD must run, killing the HUD kills the AW updates) + +Play whatever you want Stop the autoimport + Exit the Full tilt poker client (ensure it has fully stopped with ps -A) execute the following: @@ -59,14 +80,20 @@ A revised notes file (blah.merge) should automagically appear in the full tilt r If you are happy with it, replace the existing (myname.xml file) +Since the updates aren't real time, it would be ok to play the rush + session with fpdb inactive, but before quitting any of the tables, + start the HUD and wait for it to catch-up processing all the hands played. + + Summary ------- +------- This is very rough and ready, but it does what I set-out to achieve. -All feedback welcome, and if this is useful as a basis for general notes processing, then thats great. +All feedback welcome, and if this is useful as a basis for general notes + processing in future, then thats great. -As I find bugs and make improvements, I will push to the git branch. +As I find bugs and make improvements, I will push to git. Much more information below: @@ -136,12 +163,13 @@ It is hoped that due to the relatively large hand volume and relatively small although there will obviously be a number of players with no fpdb note. The aggregation parameters used for the notes are based upon the HUD parameters. + (with the exception of the hand-ranges, which uses its' own criteria (see source) -Stopping and starting the HUD will erase the previously created notes holding file. +Stopping and starting the HUD will erase the previously created notes queue file. The HUD must run, so the individual player popups need to be manually relocated. -Although hard-coded for micro RUSH tablenames, the auxilliary window will +Although hard-coded for micro RUSH tablenames, the auxilliary window could probably happily update notes of all cash and tournament players. Process overview From 927184a3f249372cb9545490cf2730013799f0fa Mon Sep 17 00:00:00 2001 From: Worros Date: Wed, 12 Jan 2011 11:45:38 +0800 Subject: [PATCH 11/32] Regression: Add FTP crasher. FTP hand that contains all the prelim information, but is actually a cancelled hand. Crashes in readButton --- .../3-Draw-Limit-USD-10-20-201101.Dead.hand.txt | Bin 0 -> 732 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pyfpdb/regression-test-files/cash/FTP/Draw/3-Draw-Limit-USD-10-20-201101.Dead.hand.txt diff --git a/pyfpdb/regression-test-files/cash/FTP/Draw/3-Draw-Limit-USD-10-20-201101.Dead.hand.txt b/pyfpdb/regression-test-files/cash/FTP/Draw/3-Draw-Limit-USD-10-20-201101.Dead.hand.txt new file mode 100644 index 0000000000000000000000000000000000000000..ef7f5815c8abf858e40c1a05614fff905dc4016f GIT binary patch literal 732 zcmaiyUrRzk5XH}P(05qK2MtQSWQA`*We){W7W8<{{z%#t@0DKq?A7n=DxwfF?9S}& z%$YOi-rt`~%{9h?`WoRYqFWXE(2HioJ>{AZ2kj}-u}-lgwbdbRMeD}eTm!0|*NZr= znSoa5le@a!Ip-RxK1wBUAV^Bv6#x?Ma*3f!>7W1#-d Date: Wed, 12 Jan 2011 11:47:42 +0800 Subject: [PATCH 12/32] FTP: Add exception handler to readButton Fix crasher in FTP (see 927184a3f249372c) --- pyfpdb/FulltiltToFpdb.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 955b55b9..761fd669 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -367,7 +367,11 @@ class Fulltilt(HandHistoryConverter): logging.warning(_("No bringin found, handid =%s") % hand.handid) def readButton(self, hand): - hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON')) + try: + hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON')) + except AttributeError, e: + # FTP has no indication that a hand is cancelled. + raise FpdbParseError(_("FTP: readButton: Failed to detect button (hand #%s cancelled?)") % hand.handid) def readHeroCards(self, hand): # streets PREFLOP, PREDRAW, and THIRD are special cases beacause From be83ab0713262cc6288e38e37ecb1c90f7ec53c7 Mon Sep 17 00:00:00 2001 From: Worros Date: Wed, 12 Jan 2011 15:09:27 +0800 Subject: [PATCH 13/32] Stars: Add 20/40 limit to table --- pyfpdb/PokerStarsToFpdb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index a46b2298..b9e1cb3e 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -56,6 +56,7 @@ class PokerStars(HandHistoryConverter): '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'), + '40.00': ('10.00', '20.00'), '40': ('10.00', '20.00'), '60.00': ('15.00', '30.00'), '60': ('15.00', '30.00'), '80.00': ('20.00', '40.00'), '80': ('20.00', '40.00'), '100.00': ('25.00', '50.00'), '100': ('25.00', '50.00'), From bd5d848934a322cdd117d3ee28273dc28247edbf Mon Sep 17 00:00:00 2001 From: Worros Date: Wed, 12 Jan 2011 17:41:03 +0800 Subject: [PATCH 14/32] FTP: Refine how players are counted as playing. Players can be noted as sitting out, but act within the hand. The summary info contains more accurate information, so use that to cull the list. --- pyfpdb/FulltiltToFpdb.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 761fd669..7a0437fe 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -76,8 +76,8 @@ class Fulltilt(HandHistoryConverter): (\s\((?PTurbo)\))?)|(?P.+)) ''' % substitutions, re.VERBOSE) re_Button = re.compile('^The button is in seat #(?P