2 # -*- coding: utf-8 -*-
4 #Copyright 2010-2011 Steffen Schaumburg
5 #This program is free software: you can redistribute it and/or modify
6 #it under the terms of the GNU Affero General Public License as published by
7 #the Free Software Foundation, version 3 of the License.
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 #GNU General Public License for more details.
14 #You should have received a copy of the GNU Affero General Public License
15 #along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #In the "official" distribution you can find the license in agpl-3.0.txt.
19 _
= L10n
.get_translation()
28 from time
import time
, strftime
37 colalias
,colshow
,colheading
,colxalign
,colformat
,coltype
= 0,1,2,3,4,5
39 class GuiTourneyPlayerStats (GuiPlayerStats
.GuiPlayerStats
):
40 def __init__(self
, config
, db
, sql
, mainwin
, debug
=True):
43 self
.cursor
= self
.db
.cursor
45 self
.main_window
= mainwin
48 self
.liststore
= [] # gtk.ListStore[] stores the contents of the grids
49 self
.listcols
= [] # gtk.TreeViewColumn[][] stores the columns in the grids
51 filters_display
= { "Heroes" : True,
66 self
.stats_frame
= None
67 self
.stats_vbox
= None
68 self
.detailFilters
= [] # the data used to enhance the sql select
70 self
.main_hbox
= gtk
.HPaned()
72 self
.filters
= TourneyFilters
.TourneyFilters(self
.db
, self
.conf
, self
.sql
, display
= filters_display
)
73 #self.filters.registerButton1Name(_("_Filters"))
74 #self.filters.registerButton1Callback(self.showDetailFilter)
75 self
.filters
.registerButton2Name(_("_Refresh Stats"))
76 self
.filters
.registerButton2Callback(self
.refreshStats
)
78 # ToDo: store in config
79 # ToDo: create popup to adjust column config
80 # columns to display, keys match column name returned by sql, values in tuple are:
81 # is column displayed, column heading, xalignment, formatting, celltype
82 self
.columns
= [ ["siteName", True, _("Site"), 0.0, "%s", "str"]
83 #,["tourney", False, _("Tourney"), 0.0, "%s", "str"] # true not allowed for this line
84 , ["category", True, _("Cat."), 0.0, "%s", "str"]
85 , ["limitType", True, _("Limit"), 0.0, "%s", "str"]
86 , ["currency", True, _("Curr."), 0.0, "%s", "str"]
87 , ["buyIn", True, _("BuyIn"), 1.0, "%3.2f", "str"]
88 , ["fee", True, _("Fee"), 1.0, "%3.2f", "str"]
89 , ["playerName", False, _("Name"), 0.0, "%s", "str"] # true not allowed for this line (set in code)
90 , ["tourneyCount", True, _("#"), 1.0, "%1.0f", "str"]
91 , ["itm", True, _("ITM%"), 1.0, "%3.2f", "str"]
92 , ["_1st", False, _("1st"), 1.0, "%1.0f", "str"]
93 , ["_2nd", True, _("2nd"), 1.0, "%1.0f", "str"]
94 , ["_3rd", True, _("3rd"), 1.0, "%1.0f", "str"]
95 , ["unknownRank", True, _("Rank?"), 1.0, "%1.0f", "str"]
96 , ["spent", True, _("Spent"), 1.0, "%3.2f", "str"]
97 , ["won", True, _("Won"), 1.0, "%3.2f", "str"]
98 , ["roi", True, _("ROI%"), 1.0, "%3.0f", "str"]
99 , ["profitPerTourney", True,_("$/Tour"), 1.0, "%3.2f", "str"]]
101 self
.stats_frame
= gtk
.Frame()
102 self
.stats_frame
.show()
104 self
.stats_vbox
= gtk
.VPaned()
105 self
.stats_vbox
.show()
106 self
.stats_frame
.add(self
.stats_vbox
)
107 # self.fillStatsFrame(self.stats_vbox)
109 #self.main_hbox.pack_start(self.filters.get_vbox())
110 #self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
111 self
.main_hbox
.pack1(self
.filters
.get_vbox())
112 self
.main_hbox
.pack2(self
.stats_frame
)
113 self
.main_hbox
.show()
116 def addGrid(self
, vbox
, query
, numTourneys
, tourneyTypes
, playerids
, sitenos
, seats
, dates
):
117 #print "start of addGrid query", query
118 #print "start of addGrid. numTourneys:",numTourneys,"tourneyTypes:", tourneyTypes, "playerids:",playerids
122 grid
=numTourneys
#TODO: should this be numTourneyTypes?
124 query
= self
.sql
.query
[query
]
125 query
= self
.refineQuery(query
, numTourneys
, tourneyTypes
, playerids
, sitenos
, seats
, dates
)
126 self
.cursor
.execute(query
)
127 result
= self
.cursor
.fetchall()
128 #print "result of the big query in addGrid:",result
129 colnames
= [desc
[0] for desc
in self
.cursor
.description
]
131 # pre-fetch some constant values:
132 #self.cols_to_show = [x for x in self.columns if x[colshow]]
133 #htourneytypeid_idx = colnames.index('tourneyTypeId')
134 self
.cols_to_show
= self
.columns
#TODO do i need above 2 lines?
136 assert len(self
.liststore
) == grid
, "len(self.liststore)="+str(len(self
.liststore
))+" grid-1="+str(grid
)
137 self
.liststore
.append( gtk
.ListStore(*([str] * len(self
.cols_to_show
))) )
138 view
= gtk
.TreeView(model
=self
.liststore
[grid
])
139 view
.set_grid_lines(gtk
.TREE_VIEW_GRID_LINES_BOTH
)
140 #vbox.pack_start(view, expand=False, padding=3)
142 textcell
= gtk
.CellRendererText()
143 textcell50
= gtk
.CellRendererText()
144 textcell50
.set_property('xalign', 0.5)
145 numcell
= gtk
.CellRendererText()
146 numcell
.set_property('xalign', 1.0)
147 assert len(self
.listcols
) == grid
148 self
.listcols
.append( [] )
150 # Create header row eg column: ("game", True, "Game", 0.0, "%s")
151 for col
, column
in enumerate(self
.cols_to_show
):
152 if column
[colalias
] == 'game' and holecards
:
153 s
= [x
for x
in self
.columns
if x
[colalias
] == 'hand'][0][colheading
]
155 s
= column
[colheading
]
156 self
.listcols
[grid
].append(gtk
.TreeViewColumn(s
))
157 view
.append_column(self
.listcols
[grid
][col
])
158 if column
[colformat
] == '%s':
159 if column
[colxalign
] == 0.0:
160 self
.listcols
[grid
][col
].pack_start(textcell
, expand
=True)
161 self
.listcols
[grid
][col
].add_attribute(textcell
, 'text', col
)
164 self
.listcols
[grid
][col
].pack_start(textcell50
, expand
=True)
165 self
.listcols
[grid
][col
].add_attribute(textcell50
, 'text', col
)
166 cellrend
= textcell50
167 self
.listcols
[grid
][col
].set_expand(True)
169 self
.listcols
[grid
][col
].pack_start(numcell
, expand
=True)
170 self
.listcols
[grid
][col
].add_attribute(numcell
, 'text', col
)
171 self
.listcols
[grid
][col
].set_expand(True)
173 #self.listcols[grid][col].set_alignment(column[colxalign]) # no effect?
174 self
.listcols
[grid
][col
].set_clickable(True)
175 self
.listcols
[grid
][col
].connect("clicked", self
.sortCols
, (col
,grid
))
177 self
.listcols
[grid
][col
].set_sort_order(gtk
.SORT_DESCENDING
)
178 self
.listcols
[grid
][col
].set_sort_indicator(True)
179 if column
[coltype
] == 'cash':
180 self
.listcols
[grid
][col
].set_cell_data_func(numcell
, self
.ledger_style_render_func
)
182 self
.listcols
[grid
][col
].set_cell_data_func(cellrend
, self
.reset_style_render_func
)
184 rows
= len(result
) # +1 for title row
188 for col
,column
in enumerate(self
.cols_to_show
):
189 if column
[colalias
] in colnames
:
190 value
= result
[sqlrow
][colnames
.index(column
[colalias
])]
193 if value
!= None and value
!= -999:
194 treerow
.append(column
[colformat
] % value
)
197 #print "addGrid, just before end of big for. grid:",grid,"treerow:",treerow
198 iter = self
.liststore
[grid
].append(treerow
)
204 def createStatsTable(self
, vbox
, tourneyTypes
, playerids
, sitenos
, seats
, dates
):
208 # Scrolled window for summary table
209 swin
= gtk
.ScrolledWindow(hadjustment
=None, vadjustment
=None)
210 swin
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
214 numTourneys
= self
.filters
.getNumTourneys()
215 self
.addGrid(swin
, 'tourneyPlayerDetailedStats', numTourneys
, tourneyTypes
, playerids
216 ,sitenos
, seats
, dates
)
218 #if 'allplayers' in groups and groups['allplayers']:
219 # can't currently do this combination so skip detailed table
224 vbox2
= gtk
.VBox(False, 0)
225 heading
= gtk
.Label(self
.filterText
['handhead'])
227 vbox2
.pack_start(heading
, expand
=False, padding
=3)
229 # Scrolled window for detailed table (display by hand)
230 swin
= gtk
.ScrolledWindow(hadjustment
=None, vadjustment
=None)
231 swin
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
233 vbox2
.pack_start(swin
, expand
=True, padding
=3)
240 self
.addGrid(swin
, 'playerDetailedStats', flags
, playerids
, sitenos
, seats
, dates
)
243 print _("Stats page displayed in %4.2f seconds") % (time() - startTime
)
244 #end def createStatsTable
246 def fillStatsFrame(self
, vbox
):
247 tourneyTypes
= self
.filters
.getTourneyTypes()
248 #tourneys = self.tourneys.getTourneys()
249 sites
= self
.filters
.getSites()
250 heroes
= self
.filters
.getHeroes()
251 siteids
= self
.filters
.getSiteIds()
252 seats
= self
.filters
.getSeats()
253 dates
= self
.filters
.getDates()
257 # Which sites are selected?
259 if sites
[site
] == True:
260 sitenos
.append(siteids
[site
])
261 _hname
= Charset
.to_utf8(heroes
[site
])
262 result
= self
.db
.get_player_id(self
.conf
, site
, _hname
)
263 if result
is not None:
264 playerids
.append(int(result
))
267 #Should probably pop up here.
268 print _("No sites selected - defaulting to PokerStars")
271 print _("No player ids found")
274 self
.createStatsTable(vbox
, tourneyTypes
, playerids
, sitenos
, seats
, dates
)
275 #end def fillStatsFrame
278 """returns the vbox of this thread"""
279 return self
.main_hbox
282 def refineQuery(self
, query
, numTourneys
, tourneyTypes
, playerids
, sitenos
, seats
, dates
):
285 #print "start of refinequery, playerids:",playerids
287 nametest
= str(tuple(playerids
))
288 nametest
= nametest
.replace("L", "")
289 nametest
= nametest
.replace(",)",")")
292 #print "refinequery, nametest after initial creation:",nametest
294 # set flag in self.columns to not show player name column
295 #[x for x in self.columns if x[0] == 'pname'][0][1] = False #TODO: fix and reactivate
297 query
= query
.replace("<nametest>", nametest
)
298 query
= query
.replace("<playerName>", pname
)
299 query
= query
.replace("<havingclause>", having
)
301 #TODO: remove, or replace with tourneytest
304 #for m in self.filters.display.items():
305 # if m[0] == 'Games' and m[1]:
310 # gametest = str(tuple(q))
311 # gametest = gametest.replace("L", "")
312 # gametest = gametest.replace(",)",")")
313 # gametest = gametest.replace("u'","'")
314 # gametest = "and gt.category in %s" % gametest
316 # gametest = "and gt.category IS NULL"
317 #query = query.replace("<game_test>", gametest)
321 for m
in self
.filters
.display
.items():
322 if m
[0] == 'Sites' and m
[1]:
326 sitetest
= str(tuple(q
))
327 sitetest
= sitetest
.replace("L", "")
328 sitetest
= sitetest
.replace(",)",")")
329 sitetest
= sitetest
.replace("u'","'")
330 sitetest
= "and tt.siteId in %s" % sitetest
#[1:-1]
332 sitetest
= "and tt.siteId IS NULL"
333 #print "refinequery, sitetest before its use for replacement:",sitetest
334 query
= query
.replace("<sitetest>", sitetest
)
337 query
= query
.replace('<seats_test>', 'between ' + str(seats
['from']) + ' and ' + str(seats
['to']))
338 if 'show' in seats
and seats
['show']:
339 query
= query
.replace('<groupbyseats>', ',h.seats')
340 query
= query
.replace('<orderbyseats>', ',h.seats')
342 query
= query
.replace('<groupbyseats>', '')
343 query
= query
.replace('<orderbyseats>', '')
345 query
= query
.replace('<seats_test>', 'between 0 and 100')
346 query
= query
.replace('<groupbyseats>', '')
347 query
= query
.replace('<orderbyseats>', '')
349 #lims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'fl']
350 #potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl']
351 #nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl']
352 #bbtest = "and ( (gt.limitType = 'fl' and gt.bigBlind in "
353 # and ( (limit and bb in()) or (nolimit and bb in ()) )
355 # blindtest = str(tuple(lims))
356 # blindtest = blindtest.replace("L", "")
357 # blindtest = blindtest.replace(",)",")")
358 # bbtest = bbtest + blindtest + ' ) '
360 # bbtest = bbtest + '(-1) ) '
361 #bbtest = bbtest + " or (gt.limitType = 'pl' and gt.bigBlind in "
363 # blindtest = str(tuple(potlims))
364 # blindtest = blindtest.replace("L", "")
365 # blindtest = blindtest.replace(",)",")")
366 # bbtest = bbtest + blindtest + ' ) '
368 # bbtest = bbtest + '(-1) ) '
369 #bbtest = bbtest + " or (gt.limitType = 'nl' and gt.bigBlind in "
371 # blindtest = str(tuple(nolims))
372 # blindtest = blindtest.replace("L", "")
373 # blindtest = blindtest.replace(",)",")")
374 # bbtest = bbtest + blindtest + ' ) )'
376 # bbtest = bbtest + '(-1) ) )'
379 # bbtest = bbtest + " and gt.type = 'ring' "
380 #elif type == 'tour':
381 #bbtest = " and gt.type = 'tour' "
383 #query = query.replace("<gtbigBlind_test>", bbtest)
385 #query = query.replace("<orderbyhgametypeId>", "")
387 # process self.detailFilters (a list of tuples)
389 #self.detailFilters = [('h.seats', 5, 6)] # for debug
390 if self
.detailFilters
:
391 for f
in self
.detailFilters
:
394 flagtest
+= ' and %s between %s and %s ' % (f
[0], str(f
[1]), str(f
[2]))
395 query
= query
.replace("<flagtest>", flagtest
)
397 # allow for differences in sql cast() function:
398 if self
.db
.backend
== self
.db
.MYSQL_INNODB
:
399 query
= query
.replace("<signed>", 'signed ')
401 query
= query
.replace("<signed>", '')
404 query
= query
.replace("<datestest>", " between '" + dates
[0] + "' and '" + dates
[1] + "'")
408 # #query = query.replace("<position>", "case hp.position when '0' then 'Btn' else hp.position end")
409 # query = query.replace("<position>", "hp.position")
410 # # set flag in self.columns to show posn column
411 # [x for x in self.columns if x[0] == 'plposition'][0][1] = True
413 # query = query.replace("<position>", "gt.base")
414 # # unset flag in self.columns to hide posn column
415 # [x for x in self.columns if x[0] == 'plposition'][0][1] = False
417 #print "query at end of refine query:", query
421 def refreshStats(self
, widget
, data
):
422 self
.last_pos
= self
.stats_vbox
.get_position()
423 try: self
.stats_vbox
.destroy()
424 except AttributeError: pass
427 #self.stats_vbox = gtk.VBox(False, 0)
428 self
.stats_vbox
= gtk
.VPaned()
429 self
.stats_vbox
.show()
430 self
.stats_frame
.add(self
.stats_vbox
)
431 self
.fillStatsFrame(self
.stats_vbox
)
432 if self
.last_pos
> 0:
433 self
.stats_vbox
.set_position(self
.last_pos
)
434 #end def refreshStats
436 def reset_style_render_func(self
, treeviewcolumn
, cell
, model
, iter):
437 cell
.set_property('foreground', None)
438 #end def reset_style_render_func
440 def sortCols(self
, col
, nums
):
442 #This doesn't actually work yet - clicking heading in top section sorts bottom section :-(
444 if not col
.get_sort_indicator() or col
.get_sort_order() == gtk
.SORT_ASCENDING
:
445 col
.set_sort_order(gtk
.SORT_DESCENDING
)
447 col
.set_sort_order(gtk
.SORT_ASCENDING
)
448 self
.liststore
[grid
].set_sort_column_id(n
, col
.get_sort_order())
449 self
.liststore
[grid
].set_sort_func(n
, self
.sortnums
, (n
,grid
))
450 for i
in xrange(len(self
.listcols
[grid
])):
451 self
.listcols
[grid
][i
].set_sort_indicator(False)
452 self
.listcols
[grid
][n
].set_sort_indicator(True)
453 # use this listcols[col].set_sort_indicator(True)
454 # to turn indicator off for other cols
456 err
= traceback
.extract_tb(sys
.exc_info()[2])
457 print _("***sortCols error: ") + str(sys
.exc_info()[1])
458 print "\n".join( [e
[0]+':'+str(e
[1])+" "+e
[2] for e
in err
] )
460 #end class GuiTourneyPlayerStats