If you only have games of a single limit type (fixed, pot, or no limit), but of more...
[fpdb-dooglus.git] / pyfpdb / RushNotesAux.py
blob312de6d5f7132edd362244eaed39f3fe4ef9528c
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, "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")
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 # 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")
142 pld.appendChild(nts)
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)
150 outputfile.close()
151 queuedom.unlink
153 if (debugmode):
154 #initialise logfiles
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")
162 debugfile.close()
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
171 if not self.active:
172 return
174 if (debugmode):
175 debugfile=open("~Rushdebug.data", "a")
176 debugfile.write(new_hand_id+"\n")
177 now = datetime.now()
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:
186 return
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}
196 xmlqueuedict = {}
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
232 PFcall="PFcall"
233 PFaggr="PFaggr"
234 PFdefend="PFdefend"
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 " +
244 ";")
245 % int(playerid))
247 for (qid, qposition, qstartcards, qstreet0Aggr, qtablename) in c.fetchall():
248 if (debugmode):
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)+
253 "\n")
255 humancards = Card.decodeStartHandValue("holdem", qstartcards)
257 if qtablename not in self.rushtables:
258 pass
259 elif qposition == "B" and qstreet0Aggr == False:
260 PFdefend=PFdefend+"/"+humancards
261 elif qstreet0Aggr == True:
262 PFaggr=PFaggr+"/"+humancards+"."+qposition
263 else:
264 PFcall=PFcall+"/"+humancards+"."+qposition
265 c.close
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"
276 "~ends~")
278 if (debugmode):
279 now = datetime.now()
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)
312 if (debugmode):
313 now = datetime.now()
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)
321 updatednotes.close()
323 xmlnotefile.unlink
325 if (debugmode):
326 now = datetime.now()
327 debugfile.write(now.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n")
328 debugfile.close()