2 # -*- coding: utf-8 -*-
5 EXPERIMENTAL - USE WITH CARE
7 Auxilliary process to push HUD data into the FullTilt player notes XML
8 This will allow a rudimentary "HUD" in rush games
10 The existing notes file will be altered by this function
12 # Copyright 2010, "Gimick" of the FPDB project fpdb.sourceforge.net
14 #This program is free software: you can redistribute it and/or modify
15 #it under the terms of the GNU Affero General Public License as published by
16 #the Free Software Foundation, version 3 of the License.
18 #This program is distributed in the hope that it will be useful,
19 #but WITHOUT ANY WARRANTY; without even the implied warranty of
20 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 #GNU General Public License for more details.
23 #You should have received a copy of the GNU Affero General Public License
24 #along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #In the "official" distribution you can find the license in agpl-3.0.txt.
27 ########################################################################
29 ##########for each hand processed, attempts to create update for player notes in FullTilt
30 ##########based upon the AW howto notes written by Ray E. Barker (nutomatic) at fpdb.sourceforge.net
31 ##########Huge thanks to Ray for his guidance and encouragement to create this !!
34 #debugmode will write logfiles for the __init__ and update_data methods
35 # writes into ./pyfpdb/~Rushdebug.*
39 # Standard Library modules
42 from xml
.dom
import minidom
43 from datetime
import datetime
46 # FreePokerDatabase modules
47 from Mucked
import Aux_Window
48 from Mucked
import Seat_Window
49 from Mucked
import Aux_Seats
54 # overload minidom methods to fix bug where \n is parsed as " ".
55 # described here: http://bugs.python.org/issue7139
58 def _write_data(writer
, data
, isAttrib
=False):
59 "Writes datachars to writer."
61 data
= data
.replace("\r", "
").replace("\n", "
")
62 data
= data
.replace("\t", "	")
64 minidom
._write
_data
= _write_data
66 def writexml(self
, writer
, indent
="", addindent
="", newl
=""):
67 # indent = current indentation
68 # addindent = indentation to add to higher levels
69 # newl = newline string
70 writer
.write(indent
+"<" + self
.tagName
)
72 attrs
= self
._get
_attributes
()
73 a_names
= attrs
.keys()
76 for a_name
in a_names
:
77 writer
.write(" %s=\"" % a_name
)
78 _write_data(writer
, attrs
[a_name
].value
, isAttrib
=True)
81 writer
.write(">%s"%(newl))
82 for node
in self
.childNodes
:
83 node
.writexml(writer
,indent
+addindent
,addindent
,newl
)
84 writer
.write("%s</%s>%s" % (indent
,self
.tagName
,newl
))
86 writer
.write("/>%s"%(newl))
87 # For an introduction to overriding instance methods, see
88 # http://irrepupavel.com/documents/python/instancemethod/
89 instancemethod
= type(minidom
.Element
.writexml
)
90 minidom
.Element
.writexml
= instancemethod(
91 writexml
, None, minidom
.Element
)
95 class RushNotes(Aux_Window
):
97 def __init__(self
, hud
, config
, params
):
103 # following line makes all the site params magically available (thanks Ray!)
105 site_params_dict
= self
.hud
.config
.get_site_parameters(self
.hud
.site
)
107 heroname
= site_params_dict
['screen_name']
108 sitename
= site_params_dict
['site_name']
109 notepath
= site_params_dict
['site_path'] # this is a temporary hijack of site-path
110 self
.heroid
= self
.hud
.db_connection
.get_player_id(self
.config
, sitename
, heroname
)
111 self
.notefile
= notepath
+ "/" + heroname
+ ".xml"
112 self
.rushtables
= ("Mach 10", "Lightning", "Celerity", "Flash", "Zoom")
114 if not os
.path
.isfile(self
.notefile
):
121 # read in existing notefile and backup with date/time in name
122 # todo change to not use dom
125 notefilebackup
= self
.notefile
+ ".backup." + now
.strftime("%Y%m%d%H%M%S")
126 xmlnotefile
= minidom
.parse(self
.notefile
)
127 outputfile
= open(notefilebackup
, 'w')
128 xmlnotefile
.writexml(outputfile
)
133 # Create a fresh queue file with skeleton XML
135 self
.queuefile
= self
.notefile
+ ".queue"
136 queuedom
= minidom
.Document()
138 pld
=queuedom
.createElement("PLAYERDATA")
139 queuedom
.appendChild(pld
)
141 nts
=queuedom
.createElement("NOTES")
144 nte
= queuedom
.createElement("NOTE")
145 nte
= queuedom
.createTextNode("\n")
146 nts
.insertBefore(nte
,None)
148 outputfile
= open(self
.queuefile
, 'w')
149 queuedom
.writexml(outputfile
)
155 debugfile
=open("~Rushdebug.init", "w")
156 debugfile
.write("conf="+str(config
)+"\n")
157 debugfile
.write("spdi="+str(site_params_dict
)+"\n")
158 debugfile
.write("para="+str(params
)+"\n")
159 debugfile
.write("hero="+heroname
+" "+str(self
.heroid
)+"\n")
160 debugfile
.write("back="+notefilebackup
+"\n")
161 debugfile
.write("queu="+self
.queuefile
+"\n")
164 open("~Rushdebug.data", "w").close()
167 def update_data(self
, new_hand_id
, db_connection
):
168 #this method called once for every hand processed
169 # self.hud.stat_dict contains the stats information for this hand
175 debugfile
=open("~Rushdebug.data", "a")
176 debugfile
.write(new_hand_id
+"\n")
178 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+ " update_data begins"+ "\n")
179 debugfile
.write("hero="+str(self
.heroid
)+"\n")
180 #debugfile.write(str(self.hud.stat_dict)+"\n")
181 debugfile
.write("table="+self
.hud
.table
.name
+"\n")
182 debugfile
.write("players="+str(self
.hud
.stat_dict
.keys())+"\n")
183 debugfile
.write("db="+str(db_connection
)+"\n")
185 if self
.hud
.table
.name
not in self
.rushtables
:
188 # Grab a list of player id's
190 handplayers
= self
.hud
.stat_dict
.keys()
193 # build a dictionary of stats text for each player in the hand (excluding the hero)
194 # xmlqueuedict contains {playername : stats text}
197 for playerid
in handplayers
:
198 # ignore hero, no notes available for hero at Full Tilt
199 if playerid
== self
.heroid
: continue
201 playername
=unicode(str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'playername')[1]))
202 # Use index[3] which is a short description
203 n
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'n')[3] + " ")
204 vpip
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'vpip')[3] + " ")
205 pfr
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'pfr')[3] + " ")
206 three_B
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'three_B')[3] + " ")
207 cbet
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'cbet')[3] + " ")
208 fbbsteal
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'f_BB_steal')[3] + " ")
210 steal
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'steal')[3] + " ")
211 ffreq1
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'ffreq1')[3] + " ")
212 agg_freq
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'agg_freq')[3] + " ")
213 BBper100
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'BBper100')[3])
214 if BBper100
[6] == "-": BBper100
=BBper100
[0:6] + "(" + BBper100
[7:] + ")"
218 # grab villain known starting hands
219 # only those where they VPIP'd, so limp in the BB will not be shown
220 # sort by hand strength. Output will show position too,
221 # so KK.1 is KK from late posn etc.
222 # ignore non-rush hands (check against known rushtablenames)
223 # cards decoding is hard-coded for holdem, so that's tuff atm
224 # three categories of known hands are shown:
225 # agression preflop hands
226 # non-aggression preflop hands
227 # bigblind called to defend hands
229 # This isn't perfect, but it isn't too bad a starting point
236 c
= db_connection
.get_cursor()
237 c
.execute(("SELECT handId, position, startCards, street0Aggr, tableName " +
238 "FROM hands, handsPlayers " +
239 "WHERE handsplayers.handId = hands.id " +
240 "AND street0VPI = 1 " +
241 "AND startCards > 0 " +
242 "AND playerId = %d " +
243 "ORDER BY startCards DESC " +
247 for (qid
, qposition
, qstartcards
, qstreet0Aggr
, qtablename
) in c
.fetchall():
249 debugfile
.write("pid, hid, pos, cards, aggr, table player"+
250 str(playerid
)+"/"+str(qid
)+"/"+str(qposition
)+"/"+
251 str(qstartcards
)+"/"+str(qstreet0Aggr
)+"/"+
252 str(qtablename
)+"/"+str(playername
)+
255 humancards
= Card
.decodeStartHandValue("holdem", qstartcards
)
257 if qtablename
not in self
.rushtables
:
259 elif qposition
== "B" and qstreet0Aggr
== False:
260 PFdefend
=PFdefend
+"/"+humancards
261 elif qstreet0Aggr
== True:
262 PFaggr
=PFaggr
+"/"+humancards
+"."+qposition
264 PFcall
=PFcall
+"/"+humancards
+"."+qposition
268 # build up final text package (top/tail with ~fpdb~ ~ends~
269 # for later search/replace by Merge module
271 xmlqueuedict
[playername
] = ("~fpdb~" + "\n" +
272 n
+ vpip
+ pfr
+ three_B
+ fbbsteal
+ "\n" +
273 steal
+ cbet
+ ffreq1
+ "\n" +
274 agg_freq
+ BBper100
+ "\n" +
275 PFcall
+"\n"+PFaggr
+"\n"+PFdefend
+"\n"
280 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+" villain data has been processed" + "\n")
281 debugfile
.write(str(xmlqueuedict
)+"\n")
284 # delaying processing of xml until now. Grab current queuefile contents and
285 # read each existing NOTE element in turn, if matched to a player in xmlqueuedict
286 # update their text in the xml and delete the dictionary item
288 xmlnotefile
= minidom
.parse(self
.queuefile
)
289 notelist
= xmlnotefile
.getElementsByTagName('NOTE')
291 for noteentry
in notelist
: #for each note in turn
292 noteplayer
= noteentry
.getAttribute("PlayerId") #extract the playername from xml
293 if noteplayer
in xmlqueuedict
: # does that player exist in the queue?
294 noteentry
.setAttribute("Text",xmlqueuedict
[noteplayer
])
295 del xmlqueuedict
[noteplayer
] #remove from list, does not need to be added later on
298 #create entries for new players (those remaining in the dictionary)
300 if len(xmlqueuedict
) > 0:
301 playerdata
=xmlnotefile
.lastChild
#move to the PLAYERDATA node (assume last one in the list)
302 notesnode
=playerdata
.childNodes
[0] #Find NOTES node
304 for newplayer
in xmlqueuedict
:
305 newentry
= xmlnotefile
.createElement("NOTE")
306 newentry
.setAttribute("PlayerId", newplayer
)
307 newentry
.setAttribute("Text", xmlqueuedict
[newplayer
])
308 notesnode
.insertBefore(newentry
,None)
309 newentry
= xmlnotefile
.createTextNode("\n")
310 notesnode
.insertBefore(newentry
,None)
314 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+" xml pre-processing complete"+ "\n")
317 # OverWrite existing xml file with updated DOM and cleanup
319 updatednotes
= open(self
.queuefile
, 'w')
320 xmlnotefile
.writexml(updatednotes
)
327 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n")