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-2011, "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", "Apollo")
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 # if queue file does not exist create a fresh queue file with skeleton XML
134 # This is possibly not totally safe, if multiple threads arrive
135 # here at the same time, but the consequences are not serious
138 self
.queuefile
= self
.notefile
+ ".queue"
139 if not (os
.path
.isfile(self
.queuefile
)):
141 queuedom
= minidom
.Document()
143 pld
=queuedom
.createElement("PLAYERDATA")
144 queuedom
.appendChild(pld
)
146 nts
=queuedom
.createElement("NOTES")
149 nte
= queuedom
.createElement("NOTE")
150 nte
= queuedom
.createTextNode("\n")
151 nts
.insertBefore(nte
,None)
153 outputfile
= open(self
.queuefile
, 'w')
154 queuedom
.writexml(outputfile
)
160 debugfile
=open("~Rushdebug.init", "a")
161 debugfile
.write("conf="+str(config
)+"\n")
162 debugfile
.write("spdi="+str(site_params_dict
)+"\n")
163 debugfile
.write("para="+str(params
)+"\n")
164 debugfile
.write("hero="+heroname
+" "+str(self
.heroid
)+"\n")
165 debugfile
.write("back="+notefilebackup
+"\n")
166 debugfile
.write("queu="+self
.queuefile
+"\n")
170 def update_data(self
, new_hand_id
, db_connection
):
171 #this method called once for every hand processed
172 # self.hud.stat_dict contains the stats information for this hand
178 debugfile
=open("~Rushdebug.data", "a")
179 debugfile
.write(new_hand_id
+"\n")
181 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+ " update_data begins"+ "\n")
182 debugfile
.write("hero="+str(self
.heroid
)+"\n")
183 #debugfile.write(str(self.hud.stat_dict)+"\n")
184 debugfile
.write("table="+self
.hud
.table
.name
+"\n")
185 debugfile
.write("players="+str(self
.hud
.stat_dict
.keys())+"\n")
186 debugfile
.write("db="+str(db_connection
)+"\n")
188 if self
.hud
.table
.name
not in self
.rushtables
:
191 # Grab a list of player id's
193 handplayers
= self
.hud
.stat_dict
.keys()
196 # build a dictionary of stats text for each player in the hand (excluding the hero)
197 # xmlqueuedict contains {playername : stats text}
200 for playerid
in handplayers
:
201 # ignore hero, no notes available for hero at Full Tilt
202 if playerid
== self
.heroid
: continue
204 playername
=unicode(str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'playername')[1]))
205 # Use index[3] which is a short description
206 n
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'n')[3] + " ")
207 vpip
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'vpip')[3] + " ")
208 pfr
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'pfr')[3] + " ")
209 three_B
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'three_B')[3] + " ")
210 four_B
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'four_B')[3] + " ")
211 cbet
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'cbet')[3] + " ")
213 fbbsteal
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'f_BB_steal')[3] + " ")
214 f_3bet
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'f_3bet')[3] + " ")
215 f_4bet
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'f_4bet')[3] + " ")
217 steal
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'steal')[3] + " ")
218 ffreq1
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'ffreq1')[3] + " ")
219 agg_freq
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'agg_freq')[3] + " ")
220 BBper100
=str(Stats
.do_stat(self
.hud
.stat_dict
, player
= playerid
, stat
= 'BBper100')[3])
221 if BBper100
[6] == "-": BBper100
=BBper100
[0:6] + "(" + BBper100
[7:] + ")"
224 # grab villain known starting hands
225 # only those where they VPIP'd, so limp in the BB will not be shown
226 # sort by hand strength. Output will show position too,
227 # so KK.1 is KK from late posn etc.
228 # ignore non-rush hands (check against known rushtablenames)
229 # cards decoding is hard-coded for holdem, so that's tuff atm
230 # three categories of known hands are shown:
231 # agression preflop hands
232 # non-aggression preflop hands
233 # bigblind called to defend hands
235 # This isn't perfect, but it isn't too bad a starting point
242 c
= db_connection
.get_cursor()
243 c
.execute(("SELECT handId, position, startCards, street0Aggr, tableName " +
244 "FROM Hands, HandsPlayers " +
245 "WHERE HandsPlayers.handId = Hands.id " +
247 "AND startCards > 0 " +
248 "AND playerId = %d " +
249 "ORDER BY startCards DESC " +
253 for (qid
, qposition
, qstartcards
, qstreet0Aggr
, qtablename
) in c
.fetchall():
255 debugfile
.write("pid, hid, pos, cards, aggr, table player"+
256 str(playerid
)+"/"+str(qid
)+"/"+str(qposition
)+"/"+
257 str(qstartcards
)+"/"+str(qstreet0Aggr
)+"/"+
258 str(qtablename
)+"/"+str(playername
)+
261 humancards
= Card
.decodeStartHandValue("holdem", qstartcards
)
263 if qtablename
not in self
.rushtables
:
265 elif qposition
== "B" and qstreet0Aggr
== False:
266 PFdefend
=PFdefend
+"/"+humancards
267 elif qstreet0Aggr
== True:
268 PFaggr
=PFaggr
+"/"+humancards
+"."+qposition
270 PFcall
=PFcall
+"/"+humancards
+"."+qposition
274 # build up final text package (top/tail with ~fpdb~ ~ends~
275 # for later search/replace by Merge module
277 xmlqueuedict
[playername
] = ("~fpdb~" + "\n" +
278 n
+ vpip
+ pfr
+ "\n" +
279 steal
+ cbet
+ fbbsteal
+ ffreq1
+ "\n" +
280 three_B
+ four_B
+ f_3bet
+ f_4bet
+ "\n" +
281 agg_freq
+ BBper100
+ "\n" +
289 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+" villain data has been processed" + "\n")
290 debugfile
.write(str(xmlqueuedict
)+"\n")
293 # delaying processing of xml until now. Grab current queuefile contents and
294 # read each existing NOTE element in turn, if matched to a player in xmlqueuedict
295 # update their text in the xml and delete the dictionary item
297 xmlnotefile
= minidom
.parse(self
.queuefile
)
298 notelist
= xmlnotefile
.getElementsByTagName('NOTE')
300 for noteentry
in notelist
: #for each note in turn
301 noteplayer
= noteentry
.getAttribute("PlayerId") #extract the playername from xml
302 if noteplayer
in xmlqueuedict
: # does that player exist in the queue?
303 noteentry
.setAttribute("Text",xmlqueuedict
[noteplayer
])
304 del xmlqueuedict
[noteplayer
] #remove from list, does not need to be added later on
307 #create entries for new players (those remaining in the dictionary)
309 if len(xmlqueuedict
) > 0:
310 playerdata
=xmlnotefile
.lastChild
#move to the PLAYERDATA node (assume last one in the list)
311 notesnode
=playerdata
.childNodes
[0] #Find NOTES node
313 for newplayer
in xmlqueuedict
:
314 newentry
= xmlnotefile
.createElement("NOTE")
315 newentry
.setAttribute("PlayerId", newplayer
)
316 newentry
.setAttribute("Text", xmlqueuedict
[newplayer
])
317 notesnode
.insertBefore(newentry
,None)
318 newentry
= xmlnotefile
.createTextNode("\n")
319 notesnode
.insertBefore(newentry
,None)
323 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+" xml pre-processing complete"+ "\n")
326 # OverWrite existing xml file with updated DOM and cleanup
328 updatednotes
= open(self
.queuefile
, 'w')
329 xmlnotefile
.writexml(updatednotes
)
336 debugfile
.write(now
.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n")