Add 'Currencies' filter to the ring player stats viewer.
[fpdb-dooglus.git] / pyfpdb / RushNotesAux.py
bloba62910cbcf734eb693880e3aff362611f497d322
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """RushNotesAux.py
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
11 """
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.*
37 debugmode = False
39 # Standard Library modules
40 import os
41 import sys
42 from xml.dom import minidom
43 from datetime import datetime
44 from time import *
46 # FreePokerDatabase modules
47 from Mucked import Aux_Window
48 from Mucked import Seat_Window
49 from Mucked import Aux_Seats
50 import Stats
51 import Card
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."
60 if isAttrib:
61 data = data.replace("\r", "&#xD;").replace("\n", "&#xA;")
62 data = data.replace("\t", "&#x9;")
63 writer.write(data)
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()
74 a_names.sort()
76 for a_name in a_names:
77 writer.write(" %s=\"" % a_name)
78 _write_data(writer, attrs[a_name].value, isAttrib=True)
79 writer.write("\"")
80 if self.childNodes:
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))
85 else:
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):
99 self.hud = hud
100 self.config = config
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)):
115 self.active = False
116 return
117 else:
118 self.active = True
121 # read in existing notefile and backup with date/time in name
122 # todo change to not use dom
124 now = datetime.now()
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)
129 outputfile.close()
130 xmlnotefile.unlink
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")
147 pld.appendChild(nts)
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)
155 outputfile.close()
156 queuedom.unlink
158 if (debugmode):
159 #initialise logfiles
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")
167 debugfile.close()
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
174 if not self.active:
175 return
177 if (debugmode):
178 debugfile=open("~Rushdebug.data", "a")
179 debugfile.write(new_hand_id+"\n")
180 now = datetime.now()
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:
189 return
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}
199 xmlqueuedict = {}
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
238 PFcall="PFcall"
239 PFaggr="PFaggr"
240 PFdefend="PFdefend"
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 " +
246 "AND street0VPI " +
247 "AND startCards > 0 " +
248 "AND playerId = %d " +
249 "ORDER BY startCards DESC " +
250 ";")
251 % int(playerid))
253 for (qid, qposition, qstartcards, qstreet0Aggr, qtablename) in c.fetchall():
254 if (debugmode):
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)+
259 "\n")
261 humancards = Card.decodeStartHandValue("holdem", qstartcards)
263 if qtablename not in self.rushtables:
264 pass
265 elif qposition == "B" and qstreet0Aggr == False:
266 PFdefend=PFdefend+"/"+humancards
267 elif qstreet0Aggr == True:
268 PFaggr=PFaggr+"/"+humancards+"."+qposition
269 else:
270 PFcall=PFcall+"/"+humancards+"."+qposition
271 c.close
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" +
282 PFcall+"\n"+
283 PFaggr+"\n"+
284 PFdefend +"\n"+
285 "~ends~")
287 if (debugmode):
288 now = datetime.now()
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)
321 if (debugmode):
322 now = datetime.now()
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)
330 updatednotes.close()
332 xmlnotefile.unlink
334 if (debugmode):
335 now = datetime.now()
336 debugfile.write(now.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n")
337 debugfile.close()