Merge branch 'master' of git://git.assembla.com/fpdboz.git

This commit is contained in:
eblade 2009-05-30 12:04:43 -04:00
commit 2e4c792bbe
27 changed files with 4456 additions and 1764 deletions

83
pyfpdb/Card.py Executable file
View File

@ -0,0 +1,83 @@
#!/usr/bin/python
#Copyright 2008 Carl Gherardi
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
def twoStartCards(value1, suit1, value2, suit2):
""" Function to convert 2 value,suit pairs into a Holdem style starting hand e.g. AQo
Hand is stored as an int 13 * x + y where (x+2) represents rank of 1st card and
(y+2) represents rank of second card (2=2 .. 14=Ace)
If x > y then pair is suited, if x < y then unsuited"""
if value1 < 2 or value2 < 2:
return(0)
if (suit1 == suit2 and value1 < value2) or (suit1 != suit2 and value2 > value1):
return(13 * (value2-2) + (value1-1))
else:
return(13 * (value1-2) + (value2-1))
def twoStartCardString(card):
""" Function to convert an int representing 2 holdem hole cards (as created by twoStartCards)
into a string like AQo """
if card <= 0:
return 'xx'
else:
card -= 1
s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
x = card/13
y = card - 13*x
if x == y: return(s[x] + s[y])
elif x > y: return(s[x] + s[y] + 's')
else: return(s[y] + s[x] + 'o')
def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4):
""" Function to convert 4 value,suit pairs into a Omaha style starting hand,
haven't decided how to encode this yet """
# This doesn't actually do anything yet - CG
# What combinations do we need to store? just cards: AA23? some suits as well e.g. when
# double suited ATcKTd? Lots more possible combos than holdem :-( 270K vs 1326? not sure
# Probably need to use this field as a key into some other table - sc
#AAKKds
#AAKKs
#AAKKr
# Is probably what we are looking for
return(0)
def cardFromValueSuit(value, suit):
""" 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As """
if suit == 'h': return(value-1)
elif suit == 'd': return(value+12)
elif suit == 'c': return(value+25)
elif suit == 's': return(value+38)
else: return(0)
def valueSuitFromCard(card):
""" Function to convert a card stored in the database (int 0-52) into value
and suit like 9s, 4c etc """
if card < 0 or card > 52:
return('')
else:
return( ['', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah'
, '2d', '3d', '4d', '5d', '6d', '7d', '8d', '9d', 'Td', 'Jd', 'Qd', 'Kd', 'Ad'
, '2c', '3c', '4c', '5c', '6c', '7c', '8c', '9c', 'Tc', 'Jc', 'Qc', 'Kc', 'Ac'
, '2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks', 'As'
][card] )

View File

@ -197,6 +197,15 @@ class Aux_window:
temp = temp + "%s" % self.layout[layout] temp = temp + "%s" % self.layout[layout]
return temp return temp
class HHC:
def __init__(self, node):
self.site = node.getAttribute("site")
self.converter = node.getAttribute("converter")
def __str__(self):
return "%s:\t%s" % (self.site, self.converter)
class Popup: class Popup:
def __init__(self, node): def __init__(self, node):
self.name = node.getAttribute("pu_name") self.name = node.getAttribute("pu_name")
@ -277,6 +286,7 @@ class Config:
self.supported_games = {} self.supported_games = {}
self.supported_databases = {} self.supported_databases = {}
self.aux_windows = {} self.aux_windows = {}
self.hhcs = {}
self.popup_windows = {} self.popup_windows = {}
# s_sites = doc.getElementsByTagName("supported_sites") # s_sites = doc.getElementsByTagName("supported_sites")
@ -299,6 +309,11 @@ class Config:
aw = Aux_window(node = aw_node) aw = Aux_window(node = aw_node)
self.aux_windows[aw.name] = aw self.aux_windows[aw.name] = aw
# s_dbs = doc.getElementsByTagName("mucked_windows")
for hhc_node in doc.getElementsByTagName("hhc"):
hhc = HHC(node = hhc_node)
self.hhcs[hhc.site] = hhc
# s_dbs = doc.getElementsByTagName("popup_windows") # s_dbs = doc.getElementsByTagName("popup_windows")
for pu_node in doc.getElementsByTagName("pu"): for pu_node in doc.getElementsByTagName("pu"):
pu = Popup(node = pu_node) pu = Popup(node = pu_node)
@ -704,6 +719,11 @@ if __name__== "__main__":
print c.aux_windows[w] print c.aux_windows[w]
print "----------- END AUX WINDOW FORMATS -----------" print "----------- END AUX WINDOW FORMATS -----------"
print "\n----------- HAND HISTORY CONVERTERS -----------"
for w in c.hhcs.keys():
print c.hhcs[w]
print "----------- END HAND HISTORY CONVERTERS -----------"
print "\n----------- POPUP WINDOW FORMATS -----------" print "\n----------- POPUP WINDOW FORMATS -----------"
for w in c.popup_windows.keys(): for w in c.popup_windows.keys():
print c.popup_windows[w] print c.popup_windows[w]

View File

@ -26,12 +26,14 @@ Create and manage the database objects.
# Standard Library modules # Standard Library modules
import sys import sys
import traceback import traceback
from datetime import datetime, date, time, timedelta
# pyGTK modules # pyGTK modules
# FreePokerTools modules # FreePokerTools modules
import Configuration import Configuration
import SQL import SQL
import Card
class Database: class Database:
def __init__(self, c, db_name, game): def __init__(self, c, db_name, game):
@ -76,8 +78,39 @@ class Database:
print "press enter to continue" print "press enter to continue"
sys.exit() sys.exit()
self.db_server = c.supported_databases[db_name].db_server
self.type = c.supported_databases[db_name].db_type self.type = c.supported_databases[db_name].db_type
self.sql = SQL.Sql(game = game, type = self.type) self.sql = SQL.Sql(game = game, type = self.type, db_server = self.db_server)
self.connection.rollback()
# To add to config:
self.hud_style = 'T' # A=All-time
# S=Session
# T=timed (last n days)
# Future values may also include:
# H=Hands (last n hands)
self.hud_hands = 1000 # Max number of hands from each player to use for hud stats
self.hud_days = 90 # Max number of days from each player to use for hud stats
self.hud_session_gap = 30 # Gap (minutes) between hands that indicates a change of session
# (hands every 2 mins for 1 hour = one session, if followed
# by a 40 minute gap and then more hands on same table that is
# a new session)
cur = self.connection.cursor()
self.hand_1day_ago = 0
cur.execute(self.sql.query['get_hand_1day_ago'])
row = cur.fetchone()
if row and row[0]:
self.hand_1day_ago = row[0]
#print "hand 1day ago =", self.hand_1day_ago
d = timedelta(days=self.hud_days)
now = datetime.utcnow() - d
self.date_ndays_ago = "d%02d%02d%02d" % (now.year-2000, now.month, now.day)
self.hand_nhands_ago = 0 # todo
#cur.execute(self.sql.query['get_table_name'], (hand_id, ))
#row = cur.fetchone()
def close_connection(self): def close_connection(self):
self.connection.close() self.connection.close()
@ -120,20 +153,26 @@ class Database:
"""Get and return the cards for each player in the hand.""" """Get and return the cards for each player in the hand."""
cards = {} # dict of cards, the key is the seat number example: {1: 'AcQd9hTs5d'} cards = {} # dict of cards, the key is the seat number example: {1: 'AcQd9hTs5d'}
c = self.connection.cursor() c = self.connection.cursor()
c.execute(self.sql.query['get_cards'], hand) c.execute(self.sql.query['get_cards'], [hand])
colnames = [desc[0] for desc in c.description] colnames = [desc[0] for desc in c.description]
cardnames = ['card1', 'card2', 'card3', 'card4', 'card5', 'card6', 'card7']
for row in c.fetchall(): for row in c.fetchall():
s_dict = {} cs = ['', '', '', '', '', '', '']
for name, val in zip(colnames, row): seat = -1
s_dict[name] = val for col,name in enumerate(colnames):
cards[s_dict['seat_number']] = (self.convert_cards(s_dict)) if name in cardnames:
cs[cardnames.index(name)] = Card.valueSuitFromCard(row[col])
elif name == 'seat_number':
seat = row[col]
if seat != -1:
cards[seat] = ''.join(cs)
return cards return cards
def get_common_cards(self, hand): def get_common_cards(self, hand):
"""Get and return the community cards for the specified hand.""" """Get and return the community cards for the specified hand."""
cards = {} cards = {}
c = self.connection.cursor() c = self.connection.cursor()
c.execute(self.sql.query['get_common_cards'], hand) c.execute(self.sql.query['get_common_cards'], [hand])
colnames = [desc[0] for desc in c.description] colnames = [desc[0] for desc in c.description]
for row in c.fetchall(): for row in c.fetchall():
s_dict = {} s_dict = {}
@ -184,24 +223,79 @@ class Database:
return winners return winners
def get_stats_from_hand(self, hand, aggregate = False): def get_stats_from_hand(self, hand, aggregate = False):
if self.hud_style == 'S':
return( self.get_stats_from_hand_session(hand) )
else: # self.hud_style == A
if aggregate:
query = 'get_stats_from_hand_aggregated'
else:
query = 'get_stats_from_hand'
if self.hud_style == 'T':
stylekey = self.date_ndays_ago
else: # assume A (all-time)
stylekey = '0000000' # all stylekey values should be higher than this
subs = (hand, hand, stylekey)
#print "get stats: hud style =", self.hud_style, "subs =", subs
c = self.connection.cursor() c = self.connection.cursor()
if aggregate: # now get the stats
query = 'get_stats_from_hand_aggregated'
subs = (hand, hand, hand)
else:
query = 'get_stats_from_hand'
subs = (hand, hand)
# now get the stats
c.execute(self.sql.query[query], subs) c.execute(self.sql.query[query], subs)
colnames = [desc[0] for desc in c.description] colnames = [desc[0] for desc in c.description]
stat_dict = {} stat_dict = {}
for row in c.fetchall(): for row in c.fetchall():
t_dict = {} t_dict = {}
for name, val in zip(colnames, row): for name, val in zip(colnames, row):
t_dict[name] = val t_dict[name.lower()] = val
# print t_dict
stat_dict[t_dict['player_id']] = t_dict stat_dict[t_dict['player_id']] = t_dict
return stat_dict
# uses query on handsplayers instead of hudcache to get stats on just this session
def get_stats_from_hand_session(self, hand):
if self.hud_style == 'S':
query = self.sql.query['get_stats_from_hand_session']
if self.db_server == 'mysql':
query = query.replace("<signed>", 'signed ')
else:
query = query.replace("<signed>", '')
else: # self.hud_style == A
return None
subs = (self.hand_1day_ago, hand)
c = self.connection.cursor()
# now get the stats
#print "sess_stats: subs =", subs, "subs[0] =", subs[0]
c.execute(query, subs)
colnames = [desc[0] for desc in c.description]
n,stat_dict = 0,{}
row = c.fetchone()
while row:
if colnames[0].lower() == 'player_id':
playerid = row[0]
else:
print "ERROR: query %s result does not have player_id as first column" % (query,)
break
for name, val in zip(colnames, row):
if not playerid in stat_dict:
stat_dict[playerid] = {}
stat_dict[playerid][name.lower()] = val
elif not name.lower() in stat_dict[playerid]:
stat_dict[playerid][name.lower()] = val
elif name.lower() not in ('hand_id', 'player_id', 'seat', 'screen_name', 'seats'):
stat_dict[playerid][name.lower()] += val
n += 1
if n >= 4000: break # todo: don't think this is needed so set nice and high
# for now - comment out or remove?
row = c.fetchone()
#print " %d rows fetched, len(stat_dict) = %d" % (n, len(stat_dict))
#print "session stat_dict =", stat_dict
return stat_dict return stat_dict
def get_player_id(self, config, site, player_name): def get_player_id(self, config, site, player_name):
@ -209,7 +303,10 @@ class Database:
c = self.connection.cursor() c = self.connection.cursor()
c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site}) c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site})
row = c.fetchone() row = c.fetchone()
return row[0] if row:
return row[0]
else:
return None
if __name__=="__main__": if __name__=="__main__":
c = Configuration.Config() c = Configuration.Config()
@ -225,16 +322,17 @@ if __name__=="__main__":
print "last hand = ", h print "last hand = ", h
hero = db_connection.get_player_id(c, 'PokerStars', 'nutOmatic') hero = db_connection.get_player_id(c, 'PokerStars', 'nutOmatic')
print "nutOmatic is id_player = %d" % hero if hero:
print "nutOmatic is id_player = %d" % hero
stat_dict = db_connection.get_stats_from_hand(h) stat_dict = db_connection.get_stats_from_hand(h)
for p in stat_dict.keys(): for p in stat_dict.keys():
print p, " ", stat_dict[p] print p, " ", stat_dict[p]
print "nutOmatics stats:" #print "nutOmatics stats:"
stat_dict = db_connection.get_stats_from_hand(h, hero) #stat_dict = db_connection.get_stats_from_hand(h, hero)
for p in stat_dict.keys(): #for p in stat_dict.keys():
print p, " ", stat_dict[p] # print p, " ", stat_dict[p]
print "cards =", db_connection.get_cards(73525) print "cards =", db_connection.get_cards(73525)
db_connection.close_connection db_connection.close_connection

View File

@ -26,8 +26,10 @@ class DerivedStats():
self.HDs = 0 self.HDs = 0
self.street0VPI = 0 self.street0VPI = 0
self.street0Aggr = 0 self.street0Aggr = 0
self.street0_3B4BChance = 0 self.street0_3BChance = 0
self.street0_3B4BDone = 0 self.street0_3BDone = 0
self.street0_4BChance = 0
self.street0_4BDone = 0
self.street1Seen = 0 self.street1Seen = 0
self.street2Seen = 0 self.street2Seen = 0

540
pyfpdb/Filters.py Normal file
View File

@ -0,0 +1,540 @@
#!/usr/bin/python
#Copyright 2008 Steffen Jobbagy-Felso
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
import threading
import pygtk
pygtk.require('2.0')
import gtk
import os
import sys
from optparse import OptionParser
from time import *
#import pokereval
import Configuration
import fpdb_db
import FpdbSQLQueries
class Filters(threading.Thread):
def __init__(self, db, settings, config, qdict, display = {},debug=True):
self.debug=debug
#print "start of GraphViewer constructor"
self.db=db
self.cursor=db.cursor
self.settings=settings
self.sql=qdict
self.conf = config
self.display = display
self.sites = {}
self.games = {}
self.limits = {}
self.seats = {}
self.siteid = {}
self.heroes = {}
self.boxes = {}
# text used on screen stored here so that it can be configured
self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits'
,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players'
,'limitstitle':'Limits:', 'seatstitle':'Number of Players:'
}
# For use in date ranges.
self.start_date = gtk.Entry(max=12)
self.end_date = gtk.Entry(max=12)
self.start_date.set_property('editable', False)
self.end_date.set_property('editable', False)
# Outer Packing box
self.mainVBox = gtk.VBox(False, 0)
playerFrame = gtk.Frame("Hero:")
playerFrame.set_label_align(0.0, 0.0)
vbox = gtk.VBox(False, 0)
self.fillPlayerFrame(vbox)
playerFrame.add(vbox)
self.boxes['player'] = vbox
sitesFrame = gtk.Frame("Sites:")
sitesFrame.set_label_align(0.0, 0.0)
vbox = gtk.VBox(False, 0)
self.fillSitesFrame(vbox)
sitesFrame.add(vbox)
self.boxes['sites'] = vbox
# Game types
gamesFrame = gtk.Frame("Games:")
gamesFrame.set_label_align(0.0, 0.0)
gamesFrame.show()
vbox = gtk.VBox(False, 0)
self.fillGamesFrame(vbox)
gamesFrame.add(vbox)
self.boxes['games'] = vbox
# Limits
limitsFrame = gtk.Frame()
limitsFrame.show()
vbox = gtk.VBox(False, 0)
self.cbLimits = {}
self.cbNoLimits = None
self.cbAllLimits = None
self.fillLimitsFrame(vbox, self.display)
limitsFrame.add(vbox)
# Seats
seatsFrame = gtk.Frame()
seatsFrame.show()
vbox = gtk.VBox(False, 0)
self.sbSeats = {}
self.fillSeatsFrame(vbox, self.display)
seatsFrame.add(vbox)
# Date
dateFrame = gtk.Frame("Date:")
dateFrame.set_label_align(0.0, 0.0)
dateFrame.show()
vbox = gtk.VBox(False, 0)
self.fillDateFrame(vbox)
dateFrame.add(vbox)
self.boxes['date'] = vbox
# Buttons
self.Button1=gtk.Button("Unnamed 1")
self.Button1.set_sensitive(False)
self.Button2=gtk.Button("Unnamed 2")
self.Button2.set_sensitive(False)
self.mainVBox.add(playerFrame)
self.mainVBox.add(sitesFrame)
self.mainVBox.add(gamesFrame)
self.mainVBox.add(limitsFrame)
self.mainVBox.add(seatsFrame)
self.mainVBox.add(dateFrame)
self.mainVBox.add(self.Button1)
self.mainVBox.add(self.Button2)
self.mainVBox.show_all()
# Should do this cleaner
if "Heroes" not in self.display or self.display["Heroes"] == False:
playerFrame.hide()
if "Sites" not in self.display or self.display["Sites"] == False:
sitesFrame.hide()
if "Games" not in self.display or self.display["Games"] == False:
gamesFrame.hide()
if "Limits" not in self.display or self.display["Limits"] == False:
limitsFrame.hide()
if "Seats" not in self.display or self.display["Seats"] == False:
seatsFrame.hide()
if "Dates" not in self.display or self.display["Dates"] == False:
dateFrame.hide()
if "Button1" not in self.display or self.display["Button1"] == False:
self.Button1.hide()
if "Button2" not in self.display or self.display["Button2"] == False:
self.Button2.hide()
def get_vbox(self):
"""returns the vbox of this thread"""
return self.mainVBox
#end def get_vbox
def getSites(self):
return self.sites
def getSiteIds(self):
return self.siteid
def getHeroes(self):
return self.heroes
def getLimits(self):
ltuple = []
for l in self.limits:
if self.limits[l] == True:
ltuple.append(l)
return ltuple
def getSeats(self):
if 'from' in self.sbSeats:
self.seats['from'] = self.sbSeats['from'].get_value_as_int()
if 'to' in self.sbSeats:
self.seats['to'] = self.sbSeats['to'].get_value_as_int()
return self.seats
def getDates(self):
return self.__get_dates()
def registerButton1Name(self, title):
self.Button1.set_label(title)
def registerButton1Callback(self, callback):
self.Button1.connect("clicked", callback, "clicked")
self.Button1.set_sensitive(True)
def registerButton2Name(self, title):
self.Button2.set_label(title)
def registerButton2Callback(self, callback):
self.Button2.connect("clicked", callback, "clicked")
self.Button2.set_sensitive(True)
def cardCallback(self, widget, data=None):
print "DEBUG: %s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
def createPlayerLine(self, hbox, site, player):
label = gtk.Label(site +" id:")
hbox.pack_start(label, False, False, 0)
pname = gtk.Entry()
pname.set_text(player)
pname.set_width_chars(20)
hbox.pack_start(pname, False, True, 0)
pname.connect("changed", self.__set_hero_name, site)
#TODO: Look at GtkCompletion - to fill out usernames
self.__set_hero_name(pname, site)
def __set_hero_name(self, w, site):
self.heroes[site] = w.get_text()
# print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
def createSiteLine(self, hbox, site):
cb = gtk.CheckButton(site)
cb.connect('clicked', self.__set_site_select, site)
cb.set_active(True)
hbox.pack_start(cb, False, False, 0)
def createGameLine(self, hbox, game):
cb = gtk.CheckButton(game)
cb.connect('clicked', self.__set_game_select, game)
hbox.pack_start(cb, False, False, 0)
def createLimitLine(self, hbox, limit, ltext):
cb = gtk.CheckButton(str(ltext))
cb.connect('clicked', self.__set_limit_select, limit)
hbox.pack_start(cb, False, False, 0)
if limit != "none":
cb.set_active(True)
return(cb)
def __set_site_select(self, w, site):
#print w.get_active()
self.sites[site] = w.get_active()
print "self.sites[%s] set to %s" %(site, self.sites[site])
def __set_game_select(self, w, game):
#print w.get_active()
self.games[game] = w.get_active()
print "self.games[%s] set to %s" %(game, self.games[game])
def __set_limit_select(self, w, limit):
#print w.get_active()
self.limits[limit] = w.get_active()
print "self.limit[%s] set to %s" %(limit, self.limits[limit])
if str(limit).isdigit():
if self.limits[limit]:
if self.cbNoLimits != None:
self.cbNoLimits.set_active(False)
else:
if self.cbAllLimits != None:
self.cbAllLimits.set_active(False)
elif limit == "all":
if self.limits[limit]:
for cb in self.cbLimits.values():
cb.set_active(True)
elif limit == "none":
if self.limits[limit]:
for cb in self.cbLimits.values():
cb.set_active(False)
def __set_seat_select(self, w, seat):
#print "__set_seat_select: seat =", seat, "active =", w.get_active()
self.seats[seat] = w.get_active()
print "self.seats[%s] set to %s" %(seat, self.seats[seat])
def fillPlayerFrame(self, vbox):
for site in self.conf.get_supported_sites():
pathHBox = gtk.HBox(False, 0)
vbox.pack_start(pathHBox, False, True, 0)
player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(pathHBox, site, player)
def fillSitesFrame(self, vbox):
for site in self.conf.get_supported_sites():
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, True, 0)
self.createSiteLine(hbox, site)
#Get db site id for filtering later
self.cursor.execute(self.sql.query['getSiteId'], (site,))
result = self.db.cursor.fetchall()
if len(result) == 1:
self.siteid[site] = result[0][0]
else:
print "Either 0 or more than one site matched - EEK"
def fillGamesFrame(self, vbox):
self.cursor.execute(self.sql.query['getGames'])
result = self.db.cursor.fetchall()
if len(result) >= 1:
for line in result:
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, True, 0)
self.createGameLine(hbox, line[0])
else:
print "INFO: No games returned from database"
def fillLimitsFrame(self, vbox, display):
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, False, 0)
lbl_title = gtk.Label(self.filterText['limitstitle'])
lbl_title.set_alignment(xalign=0.0, yalign=0.5)
hbox.pack_start(lbl_title, expand=True, padding=3)
showb = gtk.Button(label="hide", stock=None, use_underline=True)
showb.set_alignment(xalign=1.0, yalign=0.5)
showb.connect('clicked', self.__toggle_box, 'limits')
hbox.pack_start(showb, expand=False, padding=1)
vbox1 = gtk.VBox(False, 0)
vbox.pack_start(vbox1, False, False, 0)
self.boxes['limits'] = vbox1
self.cursor.execute(self.sql.query['getLimits'])
result = self.db.cursor.fetchall()
if len(result) >= 1:
hbox = gtk.HBox(True, 0)
vbox1.pack_start(hbox, False, False, 0)
vbox2 = gtk.VBox(False, 0)
hbox.pack_start(vbox2, False, False, 0)
vbox3 = gtk.VBox(False, 0)
hbox.pack_start(vbox3, False, False, 0)
for i, line in enumerate(result):
hbox = gtk.HBox(False, 0)
if i <= len(result)/2:
vbox2.pack_start(hbox, False, False, 0)
else:
vbox3.pack_start(hbox, False, False, 0)
self.cbLimits[line[0]] = self.createLimitLine(hbox, line[0], line[0])
if "LimitSep" in display and display["LimitSep"] == True and len(result) >= 2:
hbox = gtk.HBox(False, 0)
vbox1.pack_start(hbox, False, True, 0)
self.cbAllLimits = self.createLimitLine(hbox, 'all', self.filterText['limitsall'])
hbox = gtk.HBox(False, 0)
vbox1.pack_start(hbox, False, True, 0)
self.cbNoLimits = self.createLimitLine(hbox, 'none', self.filterText['limitsnone'])
hbox = gtk.HBox(False, 0)
vbox1.pack_start(hbox, False, True, 0)
cb = self.createLimitLine(hbox, 'show', self.filterText['limitsshow'])
else:
print "INFO: No games returned from database"
def fillSeatsFrame(self, vbox, display):
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, False, 0)
lbl_title = gtk.Label(self.filterText['seatstitle'])
lbl_title.set_alignment(xalign=0.0, yalign=0.5)
hbox.pack_start(lbl_title, expand=True, padding=3)
showb = gtk.Button(label="hide", stock=None, use_underline=True)
showb.set_alignment(xalign=1.0, yalign=0.5)
showb.connect('clicked', self.__toggle_box, 'seats')
hbox.pack_start(showb, expand=False, padding=1)
vbox1 = gtk.VBox(False, 0)
vbox.pack_start(vbox1, False, False, 0)
self.boxes['seats'] = vbox1
hbox = gtk.HBox(False, 0)
vbox1.pack_start(hbox, False, True, 0)
lbl_from = gtk.Label(self.filterText['seatsbetween'])
lbl_to = gtk.Label(self.filterText['seatsand'])
adj1 = gtk.Adjustment(value=2, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0)
sb1 = gtk.SpinButton(adjustment=adj1, climb_rate=0.0, digits=0)
adj2 = gtk.Adjustment(value=10, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0)
sb2 = gtk.SpinButton(adjustment=adj2, climb_rate=0.0, digits=0)
hbox.pack_start(lbl_from, expand=False, padding=3)
hbox.pack_start(sb1, False, False, 0)
hbox.pack_start(lbl_to, expand=False, padding=3)
hbox.pack_start(sb2, False, False, 0)
if "SeatSep" in display and display["SeatSep"] == True:
hbox = gtk.HBox(False, 0)
vbox1.pack_start(hbox, False, True, 0)
cb = gtk.CheckButton(self.filterText['seatsshow'])
cb.connect('clicked', self.__set_seat_select, 'show')
hbox.pack_start(cb, False, False, 0)
self.sbSeats['show'] = cb
self.seats['show'] = False
self.sbSeats['from'] = sb1
self.sbSeats['to'] = sb2
def fillCardsFrame(self, vbox):
hbox1 = gtk.HBox(True,0)
hbox1.show()
vbox.pack_start(hbox1, True, True, 0)
cards = [ "A", "K","Q","J","T","9","8","7","6","5","4","3","2" ]
for j in range(0, len(cards)):
hbox1 = gtk.HBox(True,0)
hbox1.show()
vbox.pack_start(hbox1, True, True, 0)
for i in range(0, len(cards)):
if i < (j + 1):
suit = "o"
else:
suit = "s"
button = gtk.ToggleButton("%s%s%s" %(cards[i], cards[j], suit))
button.connect("toggled", self.cardCallback, "%s%s%s" %(cards[i], cards[j], suit))
hbox1.pack_start(button, True, True, 0)
button.show()
def fillDateFrame(self, vbox):
# Hat tip to Mika Bostrom - calendar code comes from PokerStats
hbox = gtk.HBox()
vbox.pack_start(hbox, False, True, 0)
lbl_start = gtk.Label('From:')
btn_start = gtk.Button()
btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn_start.connect('clicked', self.__calendar_dialog, self.start_date)
hbox.pack_start(lbl_start, expand=False, padding=3)
hbox.pack_start(btn_start, expand=False, padding=3)
hbox.pack_start(self.start_date, expand=False, padding=2)
#New row for end date
hbox = gtk.HBox()
vbox.pack_start(hbox, False, True, 0)
lbl_end = gtk.Label(' To:')
btn_end = gtk.Button()
btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn_end.connect('clicked', self.__calendar_dialog, self.end_date)
btn_clear = gtk.Button(label=' Clear Dates ')
btn_clear.connect('clicked', self.__clear_dates)
hbox.pack_start(lbl_end, expand=False, padding=3)
hbox.pack_start(btn_end, expand=False, padding=3)
hbox.pack_start(self.end_date, expand=False, padding=2)
hbox.pack_start(btn_clear, expand=False, padding=15)
def __toggle_box(self, widget, entry):
if "Limits" not in self.display or self.display["Limits"] == False:
self.boxes[entry].hide()
elif self.boxes[entry].props.visible:
self.boxes[entry].hide()
widget.set_label("show")
else:
self.boxes[entry].show()
widget.set_label("hide")
def __calendar_dialog(self, widget, entry):
d = gtk.Window(gtk.WINDOW_TOPLEVEL)
d.set_title('Pick a date')
vb = gtk.VBox()
cal = gtk.Calendar()
vb.pack_start(cal, expand=False, padding=0)
btn = gtk.Button('Done')
btn.connect('clicked', self.__get_date, cal, entry, d)
vb.pack_start(btn, expand=False, padding=4)
d.add(vb)
d.set_position(gtk.WIN_POS_MOUSE)
d.show_all()
def __clear_dates(self, w):
self.start_date.set_text('')
self.end_date.set_text('')
def __get_dates(self):
t1 = self.start_date.get_text()
t2 = self.end_date.get_text()
if t1 == '':
t1 = '1970-01-01'
if t2 == '':
t2 = '2020-12-12'
return (t1, t2)
def __get_date(self, widget, calendar, entry, win):
# year and day are correct, month is 0..11
(year, month, day) = calendar.get_date()
month += 1
ds = '%04d-%02d-%02d' % (year, month, day)
entry.set_text(ds)
win.destroy()
def main(argv=None):
"""main can also be called in the python interpreter, by supplying the command line as the argument."""
if argv is None:
argv = sys.argv[1:]
def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit()
parser = OptionParser()
(options, sys.argv) = parser.parse_args(args = argv)
config = Configuration.Config()
db = None
settings = {}
settings.update(config.get_db_parameters())
settings.update(config.get_tv_parameters())
settings.update(config.get_import_parameters())
settings.update(config.get_default_paths())
db = fpdb_db.fpdb_db()
db.connect(settings['db-backend'],
settings['db-host'],
settings['db-databaseName'],
settings['db-user'],
settings['db-password'])
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name())
i = Filters(db, settings, config, qdict)
main_window = gtk.Window()
main_window.connect('destroy', destroy)
main_window.add(i.get_vbox())
main_window.show()
gtk.main()
if __name__ == '__main__':
sys.exit(main())

File diff suppressed because it is too large Load Diff

View File

@ -57,28 +57,55 @@ class GuiAutoImport (threading.Thread):
self.database=settings['db-databaseName'] self.database=settings['db-databaseName']
self.mainVBox=gtk.VBox(False,1) self.mainVBox=gtk.VBox(False,1)
self.mainVBox.show()
self.settingsHBox = gtk.HBox(False, 0) hbox = gtk.HBox(True, 0) # contains 2 equal vboxes
self.mainVBox.pack_start(self.settingsHBox, False, True, 0) self.mainVBox.pack_start(hbox, False, False, 0)
self.settingsHBox.show()
self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:") vbox1 = gtk.VBox(True, 0)
self.settingsHBox.pack_start(self.intervalLabel) hbox.pack_start(vbox1, True, True, 0)
self.intervalLabel.show() vbox2 = gtk.VBox(True, 0)
hbox.pack_start(vbox2, True, True, 0)
self.intervalEntry=gtk.Entry() self.intervalLabel = gtk.Label("Time between imports in seconds:")
self.intervalLabel.set_alignment(xalign=1.0, yalign=0.5)
vbox1.pack_start(self.intervalLabel, True, True, 0)
hbox = gtk.HBox(False, 0)
vbox2.pack_start(hbox, True, True, 0)
self.intervalEntry = gtk.Entry()
self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval"))) self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval")))
self.settingsHBox.pack_start(self.intervalEntry) hbox.pack_start(self.intervalEntry, False, False, 0)
self.intervalEntry.show() lbl1 = gtk.Label()
hbox.pack_start(lbl1, expand=True, fill=True)
self.addSites(self.mainVBox) lbl = gtk.Label('')
vbox1.pack_start(lbl, expand=True, fill=True)
lbl = gtk.Label('')
vbox2.pack_start(lbl, expand=True, fill=True)
self.addSites(vbox1, vbox2)
hbox = gtk.HBox(False, 0)
self.mainVBox.pack_start(hbox, expand=True, padding=3)
hbox = gtk.HBox(False, 0)
self.mainVBox.pack_start(hbox, expand=False, padding=3)
lbl1 = gtk.Label()
hbox.pack_start(lbl1, expand=True, fill=False)
self.doAutoImportBool = False self.doAutoImportBool = False
self.startButton=gtk.ToggleButton("Start Autoimport") self.startButton = gtk.ToggleButton(" _Start Autoimport ")
self.startButton.connect("clicked", self.startClicked, "start clicked") self.startButton.connect("clicked", self.startClicked, "start clicked")
self.mainVBox.add(self.startButton) hbox.pack_start(self.startButton, expand=False, fill=False)
self.startButton.show()
lbl2 = gtk.Label()
hbox.pack_start(lbl2, expand=True, fill=False)
hbox = gtk.HBox(False, 0)
hbox.show()
self.mainVBox.pack_start(hbox, expand=True, padding=3)
self.mainVBox.show_all()
#end of GuiAutoImport.__init__ #end of GuiAutoImport.__init__
@ -127,7 +154,7 @@ class GuiAutoImport (threading.Thread):
# to watch. # to watch.
if widget.get_active(): # toggled on if widget.get_active(): # toggled on
self.doAutoImportBool = True self.doAutoImportBool = True
widget.set_label(u'Stop Autoimport') widget.set_label(u' _Stop Autoimport ')
if self.pipe_to_hud is None: if self.pipe_to_hud is None:
if os.name == 'nt': if os.name == 'nt':
command = "python HUD_main.py" + " %s" % (self.database) command = "python HUD_main.py" + " %s" % (self.database)
@ -163,7 +190,7 @@ class GuiAutoImport (threading.Thread):
#print >>self.pipe_to_hud.stdin, "\n" #print >>self.pipe_to_hud.stdin, "\n"
self.pipe_to_hud.communicate('\n') # waits for process to terminate self.pipe_to_hud.communicate('\n') # waits for process to terminate
self.pipe_to_hud = None self.pipe_to_hud = None
self.startButton.set_label(u'Start Autoimport') self.startButton.set_label(u' _Start Autoimport ')
@ -177,40 +204,41 @@ class GuiAutoImport (threading.Thread):
#Create the site line given required info and setup callbacks #Create the site line given required info and setup callbacks
#enabling and disabling sites from this interface not possible #enabling and disabling sites from this interface not possible
#expects a box to layout the line horizontally #expects a box to layout the line horizontally
def createSiteLine(self, hbox, site, iconpath, hhpath, filter_name, active = True): def createSiteLine(self, hbox1, hbox2, site, iconpath, hhpath, filter_name, active = True):
label = gtk.Label(site + " auto-import:") label = gtk.Label(site + " auto-import:")
hbox.pack_start(label, False, False, 0) hbox1.pack_start(label, False, False, 3)
label.show() label.show()
dirPath=gtk.Entry() dirPath=gtk.Entry()
dirPath.set_text(hhpath) dirPath.set_text(hhpath)
hbox.pack_start(dirPath, False, True, 0) hbox1.pack_start(dirPath, True, True, 3)
dirPath.show() dirPath.show()
browseButton=gtk.Button("Browse...") browseButton=gtk.Button("Browse...")
browseButton.connect("clicked", self.browseClicked, [site] + [dirPath]) browseButton.connect("clicked", self.browseClicked, [site] + [dirPath])
hbox.pack_start(browseButton, False, False, 0) hbox2.pack_start(browseButton, False, False, 3)
browseButton.show() browseButton.show()
label = gtk.Label(site + " filter:") label = gtk.Label(' ' + site + " filter:")
hbox.pack_start(label, False, False, 0) hbox2.pack_start(label, False, False, 3)
label.show() label.show()
filter=gtk.Entry() filter=gtk.Entry()
filter.set_text(filter_name) filter.set_text(filter_name)
hbox.pack_start(filter, False, True, 0) hbox2.pack_start(filter, True, True, 3)
filter.show() filter.show()
def addSites(self, vbox): def addSites(self, vbox1, vbox2):
the_sites = self.config.get_supported_sites() the_sites = self.config.get_supported_sites()
for site in the_sites: for site in the_sites:
pathHBox = gtk.HBox(False, 0) pathHBox1 = gtk.HBox(False, 0)
vbox.pack_start(pathHBox, False, True, 0) vbox1.pack_start(pathHBox1, False, True, 0)
pathHBox.show() pathHBox2 = gtk.HBox(False, 0)
vbox2.pack_start(pathHBox2, False, True, 0)
params = self.config.get_site_parameters(site) params = self.config.get_site_parameters(site)
paths = self.config.get_default_paths(site) paths = self.config.get_default_paths(site)
self.createSiteLine(pathHBox, site, False, paths['hud-defaultPath'], params['converter'], params['enabled']) self.createSiteLine(pathHBox1, pathHBox2, site, False, paths['hud-defaultPath'], params['converter'], params['enabled'])
self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']] self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']]
if __name__== "__main__": if __name__== "__main__":

View File

@ -30,7 +30,6 @@ import gtk
# fpdb/FreePokerTools modules # fpdb/FreePokerTools modules
import fpdb_simple import fpdb_simple
import fpdb_import import fpdb_import
import fpdb_db
import Configuration import Configuration
class GuiBulkImport(): class GuiBulkImport():
@ -66,13 +65,16 @@ class GuiBulkImport():
self.importer.setDropIndexes(cb_model[cb_index][0]) self.importer.setDropIndexes(cb_model[cb_index][0])
else: else:
self.importer.setDropIndexes("auto") self.importer.setDropIndexes("auto")
hhc=self.cbfilter.get_model()[self.cbfilter.get_active()][0] sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0]
self.lab_info.set_text("Importing") self.lab_info.set_text("Importing")
self.importer.addBulkImportImportFileOrDir(self.inputFile,filter=hhc) self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename)
self.importer.setCallHud(False) self.importer.setCallHud(False)
starttime = time() starttime = time()
(stored, dups, partial, errs, ttime) = self.importer.runImport() (stored, dups, partial, errs, ttime) = self.importer.runImport()
ttime = time() - starttime
if ttime == 0:
ttime = 1
print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\ print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\
% (stored, dups, partial, errs, ttime, stored / ttime) % (stored, dups, partial, errs, ttime, stored / ttime)
self.importer.clearFileList() self.importer.clearFileList()
@ -83,8 +85,7 @@ class GuiBulkImport():
"""returns the vbox of this thread""" """returns the vbox of this thread"""
return self.vbox return self.vbox
def __init__(self, db, settings, config): def __init__(self, settings, config):
self.db = db # this is an instance of fpdb_db
self.settings = settings self.settings = settings
self.config = config self.config = config
self.importer = fpdb_import.Importer(self, self.settings, self.importer = fpdb_import.Importer(self, self.settings,
@ -175,11 +176,9 @@ class GuiBulkImport():
# ComboBox - filter # ComboBox - filter
self.cbfilter = gtk.combo_box_new_text() self.cbfilter = gtk.combo_box_new_text()
self.cbfilter.append_text("passthrough") for w in self.config.hhcs:
self.cbfilter.append_text("BetfairToFpdb") print w
self.cbfilter.append_text("EverleafToFpdb") self.cbfilter.append_text(w)
self.cbfilter.append_text("FulltiltToFpdb")
self.cbfilter.append_text("PokerStarsToFpdb")
self.cbfilter.set_active(0) self.cbfilter.set_active(0)
self.table.attach(self.cbfilter, 3, 4, 2, 3, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.cbfilter, 3, 4, 2, 3, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.cbfilter.show() self.cbfilter.show()
@ -220,8 +219,8 @@ def main(argv=None):
help="Input file in quiet mode") help="Input file in quiet mode")
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True, parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True,
help="don't start gui; deprecated (just give a filename with -f).") help="don't start gui; deprecated (just give a filename with -f).")
parser.add_option("-c", "--convert", dest="filtername", default="passthrough", metavar="FILTER", parser.add_option("-c", "--convert", dest="filtername", default="PokerStars", metavar="FILTER",
help="Conversion filter (*passthrough, FullTiltToFpdb, PokerStarsToFpdb, EverleafToFpdb)") help="Conversion filter (*Full Tilt Poker, PokerStars, Everleaf)")
parser.add_option("-x", "--failOnError", action="store_true", default=False, parser.add_option("-x", "--failOnError", action="store_true", default=False,
help="If this option is passed it quits when it encounters any error") help="If this option is passed it quits when it encounters any error")
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int", parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
@ -229,7 +228,6 @@ def main(argv=None):
(options, sys.argv) = parser.parse_args(args = argv) (options, sys.argv) = parser.parse_args(args = argv)
config = Configuration.Config() config = Configuration.Config()
db = None
settings = {} settings = {}
settings['minPrint'] = options.minPrint settings['minPrint'] = options.minPrint
@ -245,7 +243,7 @@ def main(argv=None):
print '-q is deprecated. Just use "-f filename" instead' print '-q is deprecated. Just use "-f filename" instead'
# This is because -q on its own causes an error, so -f is necessary and sufficient for cmd line use # This is because -q on its own causes an error, so -f is necessary and sufficient for cmd line use
if not options.filename: if not options.filename:
i = GuiBulkImport(db, settings, config) i = GuiBulkImport(settings, config)
main_window = gtk.Window() main_window = gtk.Window()
main_window.connect('destroy', destroy) main_window.connect('destroy', destroy)
main_window.add(i.vbox) main_window.add(i.vbox)
@ -256,7 +254,7 @@ def main(argv=None):
importer = fpdb_import.Importer(False,settings, config) importer = fpdb_import.Importer(False,settings, config)
importer.setDropIndexes("auto") importer.setDropIndexes("auto")
importer.setFailOnError(options.failOnError) importer.setFailOnError(options.failOnError)
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), filter=options.filtername) importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername)
importer.setCallHud(False) importer.setCallHud(False)
importer.runImport() importer.runImport()
importer.clearFileList() importer.clearFileList()

View File

@ -39,269 +39,9 @@ except:
import fpdb_import import fpdb_import
import fpdb_db import fpdb_db
import Filters
class GuiGraphViewer (threading.Thread): class GuiGraphViewer (threading.Thread):
def get_vbox(self):
"""returns the vbox of this thread"""
return self.mainHBox
#end def get_vbox
def clearGraphData(self):
self.fig.clf()
if self.canvas is not None:
self.canvas.destroy()
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
def generateGraph(self, widget, data):
self.clearGraphData()
sitenos = []
playerids = []
# Which sites are selected?
for site in self.sites:
if self.sites[site] == True:
sitenos.append(self.siteid[site])
self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[site],))
result = self.db.cursor.fetchall()
if len(result) == 1:
playerids.append(result[0][0])
if not sitenos:
#Should probably pop up here.
print "No sites selected - defaulting to PokerStars"
sitenos = [2]
if not playerids:
print "No player ids found"
return
#Set graph properties
self.ax = self.fig.add_subplot(111)
#Get graph data from DB
starttime = time()
line = self.getRingProfitGraph(playerids, sitenos)
print "Graph generated in: %s" %(time() - starttime)
self.ax.set_title("Profit graph for ring games")
#Set axis labels and grid overlay properites
self.ax.set_xlabel("Hands", fontsize = 12)
self.ax.set_ylabel("$", fontsize = 12)
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
if(line == None):
#TODO: Do something useful like alert user
print "No hands returned by graph query"
else:
# text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
text = "All Hands, " + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
self.ax.annotate(text,
xy=(10, -10),
xycoords='axes points',
horizontalalignment='left', verticalalignment='top',
fontsize=10)
#Draw plot
self.ax.plot(line,)
self.graphBox.add(self.canvas)
self.canvas.show()
self.exportButton.set_sensitive(True)
#end of def showClicked
def getRingProfitGraph(self, names, sites):
tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite']
# print "DEBUG: getRingProfitGraph"
start_date, end_date = self.__get_dates()
if start_date == '':
start_date = '1970-01-01'
if end_date == '':
end_date = '2020-12-12'
#Buggered if I can find a way to do this 'nicely' take a list of intergers and longs
# and turn it into a tuple readale by sql.
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
nametest = str(tuple(names))
sitetest = str(tuple(sites))
nametest = nametest.replace("L", "")
nametest = nametest.replace(",)",")")
sitetest = sitetest.replace(",)",")")
#Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf
tmp = tmp.replace("<player_test>", nametest)
tmp = tmp.replace("<site_test>", sitetest)
tmp = tmp.replace("<startdate_test>", start_date)
tmp = tmp.replace("<enddate_test>", end_date)
# print "DEBUG: sql query:"
# print tmp
self.cursor.execute(tmp)
#returns (HandId,Winnings,Costs,Profit)
winnings = self.db.cursor.fetchall()
if(winnings == ()):
return None
y=map(lambda x:float(x[3]), winnings)
line = cumsum(y)
return line/100
#end of def getRingProfitGraph
def createPlayerLine(self, hbox, site, player):
label = gtk.Label(site +" id:")
hbox.pack_start(label, False, False, 0)
label.show()
pname = gtk.Entry()
pname.set_text(player)
pname.set_width_chars(20)
hbox.pack_start(pname, False, True, 0)
pname.connect("changed", self.__set_hero_name, site)
#TODO: Look at GtkCompletion - to fill out usernames
pname.show()
self.__set_hero_name(pname, site)
def __set_hero_name(self, w, site):
self.heroes[site] = w.get_text()
# print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
def createSiteLine(self, hbox, site):
cb = gtk.CheckButton(site)
cb.connect('clicked', self.__set_site_select, site)
hbox.pack_start(cb, False, False, 0)
cb.show()
def __set_site_select(self, w, site):
# This doesn't behave as intended - self.site only allows 1 site for the moment.
print w.get_active()
self.sites[site] = w.get_active()
print "self.sites[%s] set to %s" %(site, self.sites[site])
def fillPlayerFrame(self, vbox):
for site in self.conf.supported_sites.keys():
pathHBox = gtk.HBox(False, 0)
vbox.pack_start(pathHBox, False, True, 0)
pathHBox.show()
player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(pathHBox, site, player)
def fillSitesFrame(self, vbox):
for site in self.conf.supported_sites.keys():
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
self.createSiteLine(hbox, site)
#Get db site id for filtering later
self.cursor.execute(self.sql.query['getSiteId'], (site,))
result = self.db.cursor.fetchall()
if len(result) == 1:
self.siteid[site] = result[0][0]
else:
print "Either 0 or more than one site matched - EEK"
def fillDateFrame(self, vbox):
# Hat tip to Mika Bostrom - calendar code comes from PokerStats
hbox = gtk.HBox()
vbox.pack_start(hbox, False, True, 0)
hbox.show()
lbl_start = gtk.Label('From:')
lbl_start.show()
btn_start = gtk.Button()
btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn_start.connect('clicked', self.__calendar_dialog, self.start_date)
btn_start.show()
hbox.pack_start(lbl_start, expand=False, padding=3)
hbox.pack_start(btn_start, expand=False, padding=3)
hbox.pack_start(self.start_date, expand=False, padding=2)
self.start_date.show()
#New row for end date
hbox = gtk.HBox()
vbox.pack_start(hbox, False, True, 0)
hbox.show()
lbl_end = gtk.Label(' To:')
lbl_end.show()
btn_end = gtk.Button()
btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn_end.connect('clicked', self.__calendar_dialog, self.end_date)
btn_end.show()
btn_clear = gtk.Button(label=' Clear Dates ')
btn_clear.connect('clicked', self.__clear_dates)
btn_clear.show()
hbox.pack_start(lbl_end, expand=False, padding=3)
hbox.pack_start(btn_end, expand=False, padding=3)
hbox.pack_start(self.end_date, expand=False, padding=2)
self.end_date.show()
hbox.pack_start(btn_clear, expand=False, padding=15)
def __calendar_dialog(self, widget, entry):
d = gtk.Window(gtk.WINDOW_TOPLEVEL)
d.set_title('Pick a date')
vb = gtk.VBox()
cal = gtk.Calendar()
vb.pack_start(cal, expand=False, padding=0)
btn = gtk.Button('Done')
btn.connect('clicked', self.__get_date, cal, entry, d)
vb.pack_start(btn, expand=False, padding=4)
d.add(vb)
d.set_position(gtk.WIN_POS_MOUSE)
d.show_all()
def __clear_dates(self, w):
self.start_date.set_text('')
self.end_date.set_text('')
def __get_dates(self):
t1 = self.start_date.get_text()
t2 = self.end_date.get_text()
return (t1, t2)
def __get_date(self, widget, calendar, entry, win):
# year and day are correct, month is 0..11
(year, month, day) = calendar.get_date()
month += 1
ds = '%04d-%02d-%02d' % (year, month, day)
entry.set_text(ds)
win.destroy()
def exportGraph (self, widget, data):
if self.fig is None:
return # Might want to disable export button until something has been generated.
dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
#TODO: Suggest path and filename to start with
response = dia_chooser.run()
if response == gtk.RESPONSE_OK:
self.exportDir = dia_chooser.get_filename()
print "DEBUG: self.exportDir = %s" %(self.exportDir)
elif response == gtk.RESPONSE_CANCEL:
print 'Closed, no graph exported'
dia_chooser.destroy()
#TODO: Check to see if file exists
#NOTE: Dangerous - will happily overwrite any file we have write access too
#TODO: This asks for a directory but will take a filename and overwrite it.
self.fig.savefig(self.exportDir, format="png")
def __init__(self, db, settings, querylist, config, debug=True): def __init__(self, db, settings, querylist, config, debug=True):
"""Constructor for GraphViewer""" """Constructor for GraphViewer"""
@ -313,21 +53,28 @@ class GuiGraphViewer (threading.Thread):
self.sql=querylist self.sql=querylist
self.conf = config self.conf = config
self.sites = {} filters_display = { "Heroes" : True,
self.siteid = {} "Sites" : True,
self.heroes = {} "Games" : True,
"Limits" : True,
"Seats" : False,
"Dates" : True,
"Button1" : True,
"Button2" : True
}
# For use in date ranges. self.filters = Filters.Filters(db, settings, config, querylist, display = filters_display)
self.start_date = gtk.Entry(max=12) self.filters.registerButton1Name("Refresh Graph")
self.end_date = gtk.Entry(max=12) self.filters.registerButton1Callback(self.generateGraph)
self.start_date.set_property('editable', False) self.filters.registerButton2Name("Export to File")
self.end_date.set_property('editable', False) self.filters.registerButton2Callback(self.exportGraph)
self.mainHBox = gtk.HBox(False, 0) self.mainHBox = gtk.HBox(False, 0)
self.mainHBox.show() self.mainHBox.show()
self.leftPanelBox = gtk.VBox(False, 0) self.leftPanelBox = self.filters.get_vbox()
self.graphBox = gtk.VBox(False, 0) self.graphBox = gtk.VBox(False, 0)
self.graphBox.show()
self.hpane = gtk.HPaned() self.hpane = gtk.HPaned()
self.hpane.pack1(self.leftPanelBox) self.hpane.pack1(self.leftPanelBox)
@ -336,55 +83,15 @@ class GuiGraphViewer (threading.Thread):
self.mainHBox.add(self.hpane) self.mainHBox.add(self.hpane)
playerFrame = gtk.Frame("Hero:")
playerFrame.set_label_align(0.0, 0.0)
playerFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillPlayerFrame(vbox)
playerFrame.add(vbox)
sitesFrame = gtk.Frame("Sites:")
sitesFrame.set_label_align(0.0, 0.0)
sitesFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillSitesFrame(vbox)
sitesFrame.add(vbox)
dateFrame = gtk.Frame("Date:")
dateFrame.set_label_align(0.0, 0.0)
dateFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillDateFrame(vbox)
dateFrame.add(vbox)
graphButton=gtk.Button("Generate Graph")
graphButton.connect("clicked", self.generateGraph, "cliced data")
graphButton.show()
self.fig = None self.fig = None
self.exportButton=gtk.Button("Export to File") #self.exportButton.set_sensitive(False)
self.exportButton.connect("clicked", self.exportGraph, "show clicked")
self.exportButton.set_sensitive(False)
self.exportButton.show()
self.leftPanelBox.add(playerFrame)
self.leftPanelBox.add(sitesFrame)
self.leftPanelBox.add(dateFrame)
self.leftPanelBox.add(graphButton)
self.leftPanelBox.add(self.exportButton)
self.leftPanelBox.show()
self.graphBox.show()
self.fig = Figure(figsize=(5,4), dpi=100) self.fig = Figure(figsize=(5,4), dpi=100)
self.canvas = None self.canvas = None
self.db.db.rollback()
################################# #################################
# #
# self.db.cursor.execute("""select UNIX_TIMESTAMP(handStart) as time, id from Hands ORDER BY time""") # self.db.cursor.execute("""select UNIX_TIMESTAMP(handStart) as time, id from Hands ORDER BY time""")
@ -412,3 +119,144 @@ class GuiGraphViewer (threading.Thread):
# print "Total: ", total # print "Total: ", total
################################# #################################
def get_vbox(self):
"""returns the vbox of this thread"""
return self.mainHBox
#end def get_vbox
def clearGraphData(self):
self.fig.clf()
if self.canvas is not None:
self.canvas.destroy()
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
def generateGraph(self, widget, data):
self.clearGraphData()
sitenos = []
playerids = []
sites = self.filters.getSites()
heroes = self.filters.getHeroes()
siteids = self.filters.getSiteIds()
limits = self.filters.getLimits()
# Which sites are selected?
for site in sites:
if sites[site] == True:
sitenos.append(siteids[site])
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
result = self.db.cursor.fetchall()
if len(result) == 1:
playerids.append(result[0][0])
if not sitenos:
#Should probably pop up here.
print "No sites selected - defaulting to PokerStars"
sitenos = [2]
if not playerids:
print "No player ids found"
return
if not limits:
print "No limits found"
return
#Set graph properties
self.ax = self.fig.add_subplot(111)
#Get graph data from DB
starttime = time()
line = self.getRingProfitGraph(playerids, sitenos, limits)
print "Graph generated in: %s" %(time() - starttime)
self.ax.set_title("Profit graph for ring games")
#Set axis labels and grid overlay properites
self.ax.set_xlabel("Hands", fontsize = 12)
self.ax.set_ylabel("$", fontsize = 12)
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
if line == None or line == []:
#TODO: Do something useful like alert user
print "No hands returned by graph query"
else:
# text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
text = "All Hands, " + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
self.ax.annotate(text,
xy=(10, -10),
xycoords='axes points',
horizontalalignment='left', verticalalignment='top',
fontsize=10)
#Draw plot
self.ax.plot(line,)
self.graphBox.add(self.canvas)
self.canvas.show()
#self.exportButton.set_sensitive(True)
#end of def showClicked
def getRingProfitGraph(self, names, sites, limits):
tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite']
# print "DEBUG: getRingProfitGraph"
start_date, end_date = self.filters.getDates()
#Buggered if I can find a way to do this 'nicely' take a list of intergers and longs
# and turn it into a tuple readale by sql.
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
nametest = str(tuple(names))
sitetest = str(tuple(sites))
limittest = str(tuple(limits))
nametest = nametest.replace("L", "")
nametest = nametest.replace(",)",")")
sitetest = sitetest.replace(",)",")")
limittest = limittest.replace("L", "")
limittest = limittest.replace(",)",")")
#Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf
tmp = tmp.replace("<player_test>", nametest)
tmp = tmp.replace("<site_test>", sitetest)
tmp = tmp.replace("<startdate_test>", start_date)
tmp = tmp.replace("<enddate_test>", end_date)
tmp = tmp.replace("<limit_test>", limittest)
#print "DEBUG: sql query:"
#print tmp
self.cursor.execute(tmp)
#returns (HandId,Winnings,Costs,Profit)
winnings = self.db.cursor.fetchall()
self.db.db.rollback()
if(winnings == ()):
return None
y=map(lambda x:float(x[3]), winnings)
line = cumsum(y)
return line/100
#end of def getRingProfitGraph
def exportGraph (self, widget, data):
if self.fig is None:
return # Might want to disable export button until something has been generated.
dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
#TODO: Suggest path and filename to start with
response = dia_chooser.run()
if response == gtk.RESPONSE_OK:
self.exportDir = dia_chooser.get_filename()
print "DEBUG: self.exportDir = %s" %(self.exportDir)
elif response == gtk.RESPONSE_CANCEL:
print 'Closed, no graph exported'
dia_chooser.destroy()
#TODO: Check to see if file exists
#NOTE: Dangerous - will happily overwrite any file we have write access too
#TODO: This asks for a directory but will take a filename and overwrite it.
self.fig.savefig(self.exportDir, format="png")

View File

@ -20,158 +20,421 @@ import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import os import os
from time import time, strftime
import Card
import fpdb_import import fpdb_import
import fpdb_db import fpdb_db
import Filters
import FpdbSQLQueries import FpdbSQLQueries
class GuiPlayerStats (threading.Thread): class GuiPlayerStats (threading.Thread):
def get_vbox(self): def __init__(self, config, querylist, mainwin, debug=True):
"""returns the vbox of this thread"""
return self.main_hbox
def toggleCallback(self, widget, data=None):
# print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
self.activesite = data
print "DEBUG: activesite set to %s" %(self.activesite)
def refreshStats(self, widget, data):
try: self.stats_table.destroy()
except AttributeError: pass
self.fillStatsFrame(self.stats_frame)
def fillStatsFrame(self, vbox):
# Get currently active site and grab playerid
tmp = self.sql.query['playerStats']
result = self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[self.activesite],))
result = self.cursor.fetchall()
if not result == ():
pid = result[0][0]
pid = result[0][0]
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
self.cursor.execute(tmp)
result = self.cursor.fetchall()
cols = 16
rows = len(result)+1 # +1 for title row
self.stats_table = gtk.Table(rows, cols, False)
self.stats_table.set_col_spacings(4)
self.stats_table.show()
vbox.add(self.stats_table)
# Create header row
titles = ("Game", "Hands", "VPIP", "PFR", "Saw_F", "SawSD", "WtSDwsF", "W$SD", "FlAFq", "TuAFq", "RvAFq", "PoFAFq", "Net($)", "BB/100", "$/hand", "Variance")
col = 0
row = 0
for t in titles:
l = gtk.Label(titles[col])
l.show()
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
col +=1
for row in range(rows-1):
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
for col in range(cols):
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
if result[row][col]:
l = gtk.Label(result[row][col])
else:
l = gtk.Label(' ')
if col == 0:
l.set_alignment(xalign=0.0, yalign=0.5)
else:
l.set_alignment(xalign=1.0, yalign=0.5)
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
l.show()
eb.show()
self.fdb.db.commit()
#end def fillStatsFrame(self, vbox):
def fillPlayerFrame(self, vbox):
for site in self.conf.supported_sites.keys():
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(hbox, site, player)
hbox = gtk.HBox(False, 0)
button = gtk.Button("Refresh")
button.connect("clicked", self.refreshStats, False)
button.show()
hbox.add(button)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
def createPlayerLine(self, hbox, site, player):
if(self.buttongroup == None):
button = gtk.RadioButton(None, site + " id:")
button.set_active(True)
self.buttongroup = button
self.activesite = site
else:
button = gtk.RadioButton(self.buttongroup, site + " id:")
hbox.pack_start(button, True, True, 0)
button.connect("toggled", self.toggleCallback, site)
button.show()
pname = gtk.Entry()
pname.set_text(player)
pname.set_width_chars(20)
hbox.pack_start(pname, False, True, 0)
pname.connect("changed", self.__set_hero_name, site)
#TODO: Look at GtkCompletion - to fill out usernames
pname.show()
self.__set_hero_name(pname, site)
def __set_hero_name(self, w, site):
self.heroes[site] = w.get_text()
def __init__(self, db, config, querylist, debug=True):
self.debug=debug self.debug=debug
self.conf=config self.conf=config
self.main_window=mainwin
self.MYSQL_INNODB = 2
self.PGSQL = 3
self.SQLITE = 4
# create new db connection to avoid conflicts with other threads # create new db connection to avoid conflicts with other threads
self.fdb = fpdb_db.fpdb_db() self.db = fpdb_db.fpdb_db()
self.fdb.do_connect(self.conf) self.db.do_connect(self.conf)
self.cursor=self.fdb.cursor self.cursor=self.db.cursor
self.sql = querylist self.sql = querylist
self.activesite = None settings = {}
self.buttongroup = None settings.update(config.get_db_parameters())
settings.update(config.get_tv_parameters())
settings.update(config.get_import_parameters())
settings.update(config.get_default_paths())
# text used on screen stored here so that it can be configured
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
}
filters_display = { "Heroes" : True,
"Sites" : True,
"Games" : False,
"Limits" : True,
"LimitSep" : True,
"Seats" : True,
"SeatSep" : True,
"Dates" : False,
"Groups" : True,
"Button1" : True,
"Button2" : True
}
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
self.filters.registerButton1Name("_Filters")
self.filters.registerButton1Callback(self.showDetailFilter)
self.filters.registerButton2Name("_Refresh")
self.filters.registerButton2Callback(self.refreshStats)
# ToDo: store in config
# ToDo: create popup to adjust column config
# columns to display, keys match column name returned by sql, values in tuple are:
# is column displayed, column heading, xalignment, formatting
self.columns = [ ("game", True, "Game", 0.0, "%s")
, ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line
, ("n", True, "Hds", 1.0, "%d")
, ("avgseats", True, "Seats", 1.0, "%3.1f")
, ("vpip", True, "VPIP", 1.0, "%3.1f")
, ("pfr", True, "PFR", 1.0, "%3.1f")
, ("pf3", True, "PF3", 1.0, "%3.1f")
, ("steals", True, "Steals", 1.0, "%3.1f")
, ("saw_f", True, "Saw_F", 1.0, "%3.1f")
, ("sawsd", True, "SawSD", 1.0, "%3.1f")
, ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f")
, ("wmsd", True, "W$SD", 1.0, "%3.1f")
, ("flafq", True, "FlAFq", 1.0, "%3.1f")
, ("tuafq", True, "TuAFq", 1.0, "%3.1f")
, ("rvafq", True, "RvAFq", 1.0, "%3.1f")
, ("pofafq", False, "PoFAFq", 1.0, "%3.1f")
, ("net", True, "Net($)", 1.0, "%6.2f")
, ("bbper100", True, "BB/100", 1.0, "%4.2f")
, ("rake", True, "Rake($)", 1.0, "%6.2f")
, ("variance", True, "Variance", 1.0, "%5.2f")
]
# Detail filters: This holds the data used in the popup window, extra values are
# added at the end of these lists during processing
# sql test, screen description, min, max
self.handtests = [ # already in filter class : ['h.seats', 'Number of Players', 2, 10]
['h.maxSeats', 'Size of Table', 2, 10]
,['h.playersVpi', 'Players who VPI', 0, 10]
,['h.playersAtStreet1', 'Players at Flop', 0, 10]
,['h.playersAtStreet2', 'Players at Turn', 0, 10]
,['h.playersAtStreet3', 'Players at River', 0, 10]
,['h.playersAtStreet4', 'Players at Street7', 0, 10]
,['h.playersAtShowdown', 'Players at Showdown', 0, 10]
,['h.street0Raises', 'Bets to See Flop', 0, 5]
,['h.street1Raises', 'Bets to See Turn', 0, 5]
,['h.street2Raises', 'Bets to See River', 0, 5]
,['h.street3Raises', 'Bets to See Street7', 0, 5]
,['h.street4Raises', 'Bets to See Showdown', 0, 5]
]
self.heroes = {}
self.stat_table = None
self.stats_frame = None self.stats_frame = None
self.stats_vbox = None
self.detailFilters = [] # the data used to enhance the sql select
self.main_hbox = gtk.HBox(False, 0) self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show() self.main_hbox.show()
playerFrame = gtk.Frame("Hero:") self.stats_frame = gtk.Frame()
playerFrame.set_label_align(0.0, 0.0)
playerFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillPlayerFrame(vbox)
playerFrame.add(vbox)
statsFrame = gtk.Frame("Stats:")
statsFrame.set_label_align(0.0, 0.0)
statsFrame.show()
self.stats_frame = gtk.VBox(False, 0)
self.stats_frame.show() self.stats_frame.show()
self.fillStatsFrame(self.stats_frame) self.stats_vbox = gtk.VBox(False, 0)
statsFrame.add(self.stats_frame) self.stats_vbox.show()
self.stats_frame.add(self.stats_vbox)
self.fillStatsFrame(self.stats_vbox)
self.main_hbox.pack_start(self.filters.get_vbox())
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
# make sure Hand column is not displayed
[x for x in self.columns if x[0] == 'hand'][0][1] == False
def get_vbox(self):
"""returns the vbox of this thread"""
return self.main_hbox
def refreshStats(self, widget, data):
try: self.stats_vbox.destroy()
except AttributeError: pass
self.stats_vbox = gtk.VBox(False, 0)
self.stats_vbox.show()
self.stats_frame.add(self.stats_vbox)
self.fillStatsFrame(self.stats_vbox)
def fillStatsFrame(self, vbox):
sites = self.filters.getSites()
heroes = self.filters.getHeroes()
siteids = self.filters.getSiteIds()
limits = self.filters.getLimits()
seats = self.filters.getSeats()
sitenos = []
playerids = []
# Which sites are selected?
for site in sites:
if sites[site] == True:
sitenos.append(siteids[site])
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
result = self.db.cursor.fetchall()
if len(result) == 1:
playerids.append(result[0][0])
if not sitenos:
#Should probably pop up here.
print "No sites selected - defaulting to PokerStars"
sitenos = [2]
if not playerids:
print "No player ids found"
return
if not limits:
print "No limits found"
return
self.createStatsTable(vbox, playerids, sitenos, limits, seats)
def createStatsTable(self, vbox, playerids, sitenos, limits, seats):
starttime = time()
# Display summary table at top of page
# 3rd parameter passes extra flags, currently includes:
# holecards - whether to display card breakdown (True/False)
flags = [False]
self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, seats)
# Separator
sep = gtk.HSeparator()
vbox.pack_start(sep, expand=False, padding=3)
sep.show_now()
vbox.show_now()
heading = gtk.Label(self.filterText['handhead'])
heading.show()
vbox.pack_start(heading, expand=False, padding=3)
# Scrolled window for detailed table (display by hand)
swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)
swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
swin.show()
vbox.pack_start(swin, expand=True, padding=3)
vbox1 = gtk.VBox(False, 0)
vbox1.show()
swin.add_with_viewport(vbox1)
# Detailed table
flags = [True]
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats)
self.db.db.commit()
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
#end def fillStatsFrame(self, vbox):
def addTable(self, vbox, query, flags, playerids, sitenos, limits, seats):
row = 0
sqlrow = 0
colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4
if not flags: holecards = False
else: holecards = flags[0]
self.stats_table = gtk.Table(1, 1, False)
self.stats_table.set_col_spacings(4)
self.stats_table.show()
tmp = self.sql.query[query]
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats)
self.cursor.execute(tmp)
result = self.cursor.fetchall()
colnames = [desc[0].lower() for desc in self.cursor.description]
# pre-fetch some constant values:
cols_to_show = [x for x in self.columns if x[colshow]]
hgametypeid_idx = colnames.index('hgametypeid')
liststore = gtk.ListStore(*([str] * len(cols_to_show)))
view = gtk.TreeView(model=liststore)
view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
vbox.pack_start(view, expand=False, padding=3)
textcell = gtk.CellRendererText()
numcell = gtk.CellRendererText()
numcell.set_property('xalign', 1.0)
listcols = []
# Create header row eg column: ("game", True, "Game", 0.0, "%s")
for col, column in enumerate(cols_to_show):
if column[colalias] == 'game' and holecards:
s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading]
else:
s = column[colheading]
listcols.append(gtk.TreeViewColumn(s))
view.append_column(listcols[col])
if column[colformat] == '%s':
if col == 1 and holecards:
listcols[col].pack_start(textcell, expand=True)
else:
listcols[col].pack_start(textcell, expand=True)
listcols[col].add_attribute(textcell, 'text', col)
listcols[col].set_expand(True)
else:
listcols[col].pack_start(numcell, expand=True)
listcols[col].add_attribute(numcell, 'text', col)
listcols[col].set_alignment(1.0)
listcols[col].set_expand(True)
rows = len(result) # +1 for title row
while sqlrow < rows:
treerow = []
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
for col,column in enumerate(cols_to_show):
if column[colalias] in colnames:
value = result[sqlrow][colnames.index(column[colalias])]
else:
if column[colalias] == 'game':
if holecards:
value = Card.twoStartCardString( result[sqlrow][hgametypeid_idx] )
else:
minbb = result[sqlrow][colnames.index('minbigblind')]
maxbb = result[sqlrow][colnames.index('maxbigblind')]
value = result[sqlrow][colnames.index('limittype')] + ' ' \
+ result[sqlrow][colnames.index('category')].title() + ' ' \
+ result[sqlrow][colnames.index('name')] + ' $'
if 100 * int(minbb/100.0) != minbb:
value += '%.2f' % (minbb/100.0)
else:
value += '%.0f' % (minbb/100.0)
if minbb != maxbb:
if 100 * int(maxbb/100.0) != maxbb:
value += ' - $' + '%.2f' % (maxbb/100.0)
else:
value += ' - $' + '%.0f' % (maxbb/100.0)
else:
continue
if value and value != -999:
treerow.append(column[colformat] % value)
else:
treerow.append(' ')
iter = liststore.append(treerow)
sqlrow += 1
row += 1
vbox.show_all()
#end def addTable(self, query, vars, playerids, sitenos, limits, seats):
def refineQuery(self, query, flags, playerids, sitenos, limits, seats):
if not flags: holecards = False
else: holecards = flags[0]
if playerids:
nametest = str(tuple(playerids))
nametest = nametest.replace("L", "")
nametest = nametest.replace(",)",")")
query = query.replace("<player_test>", nametest)
else:
query = query.replace("<player_test>", "1 = 2")
if seats:
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
if 'show' in seats and seats['show']:
query = query.replace('<groupbyseats>', ',h.seats')
query = query.replace('<orderbyseats>', ',h.seats')
else:
query = query.replace('<groupbyseats>', '')
query = query.replace('<orderbyseats>', '')
else:
query = query.replace('<seats_test>', 'between 0 and 100')
query = query.replace('<groupbyseats>', '')
query = query.replace('<orderbyseats>', '')
if [x for x in limits if str(x).isdigit()]:
blindtest = str(tuple([x for x in limits if str(x).isdigit()]))
blindtest = blindtest.replace("L", "")
blindtest = blindtest.replace(",)",")")
query = query.replace("<gtbigBlind_test>", " and gt.bigBlind in " + blindtest + " ")
else:
query = query.replace("<gtbigBlind_test>", "")
if holecards: # pinch level variables for hole card query
query = query.replace("<hgameTypeId>", "hp.startcards")
query = query.replace("<orderbyhgameTypeId>", ",hgameTypeId desc")
else:
query = query.replace("<orderbyhgameTypeId>", "")
groupLevels = "show" not in str(limits)
if groupLevels:
query = query.replace("<hgameTypeId>", "-1")
else:
query = query.replace("<hgameTypeId>", "h.gameTypeId")
# process self.detailFilters (a list of tuples)
flagtest = ''
#self.detailFilters = [('h.seats', 5, 6)] # for debug
if self.detailFilters:
for f in self.detailFilters:
if len(f) == 3:
# X between Y and Z
flagtest += ' and %s between %s and %s ' % (f[0], str(f[1]), str(f[2]))
query = query.replace("<flagtest>", flagtest)
# allow for differences in sql cast() function:
if self.db.backend == self.MYSQL_INNODB:
query = query.replace("<signed>", 'signed ')
else:
query = query.replace("<signed>", '')
#print "query =\n", query
return(query)
#end def refineQuery(self, query, playerids, sitenos, limits):
def showDetailFilter(self, widget, data):
detailDialog = gtk.Dialog(title="Detailed Filters", parent=self.main_window
,flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
,buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
handbox = gtk.VBox(True, 0)
detailDialog.vbox.pack_start(handbox, False, False, 0)
handbox.show()
label = gtk.Label("Hand Filters:")
handbox.add(label)
label.show()
betweenFilters = []
for htest in self.handtests:
hbox = gtk.HBox(False, 0)
handbox.pack_start(hbox, False, False, 0)
hbox.show()
cb = gtk.CheckButton()
lbl_from = gtk.Label(htest[1])
lbl_from.set_alignment(xalign=0.0, yalign=0.5)
lbl_tween = gtk.Label('between')
lbl_to = gtk.Label('and')
adj1 = gtk.Adjustment(value=htest[2], lower=0, upper=10, step_incr=1, page_incr=1, page_size=0)
sb1 = gtk.SpinButton(adjustment=adj1, climb_rate=0.0, digits=0)
adj2 = gtk.Adjustment(value=htest[3], lower=2, upper=10, step_incr=1, page_incr=1, page_size=0)
sb2 = gtk.SpinButton(adjustment=adj2, climb_rate=0.0, digits=0)
for df in [x for x in self.detailFilters if x[0] == htest[0]]:
cb.set_active(True)
hbox.pack_start(cb, expand=False, padding=3)
hbox.pack_start(lbl_from, expand=True, padding=3)
hbox.pack_start(lbl_tween, expand=False, padding=3)
hbox.pack_start(sb1, False, False, 0)
hbox.pack_start(lbl_to, expand=False, padding=3)
hbox.pack_start(sb2, False, False, 0)
cb.show()
lbl_from.show()
lbl_tween.show()
sb1.show()
lbl_to.show()
sb2.show()
htest[4:7] = [cb,sb1,sb2]
response = detailDialog.run()
if response == gtk.RESPONSE_ACCEPT:
self.detailFilters = []
for ht in self.handtests:
if ht[4].get_active():
self.detailFilters.append( (ht[0], ht[5].get_value_as_int(), ht[6].get_value_as_int()) )
ht[2],ht[3] = ht[5].get_value_as_int(), ht[6].get_value_as_int()
print "detailFilters =", self.detailFilters
self.refreshStats(None, None)
detailDialog.destroy()
self.main_hbox.pack_start(playerFrame)
self.main_hbox.pack_start(statsFrame)

View File

@ -23,9 +23,80 @@ import os
import fpdb_import import fpdb_import
import fpdb_db import fpdb_db
import Filters
import FpdbSQLQueries import FpdbSQLQueries
class GuiPositionalStats (threading.Thread): class GuiPositionalStats (threading.Thread):
def __init__(self, config, querylist, debug=True):
self.debug=debug
self.conf=config
self.MYSQL_INNODB = 2
self.PGSQL = 3
self.SQLITE = 4
# create new db connection to avoid conflicts with other threads
self.db = fpdb_db.fpdb_db()
self.db.do_connect(self.conf)
self.cursor=self.db.cursor
self.sql = querylist
settings = {}
settings.update(config.get_db_parameters())
settings.update(config.get_tv_parameters())
settings.update(config.get_import_parameters())
settings.update(config.get_default_paths())
filters_display = { "Heroes" : True,
"Sites" : True,
"Games" : False,
"Limits" : True,
"LimitSep" : True,
"Seats" : True,
"SeatSep" : True,
"Dates" : True,
"Button1" : True,
"Button2" : False
}
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
self.filters.registerButton1Name("Refresh")
self.filters.registerButton1Callback(self.refreshStats)
self.stat_table = None
self.stats_frame = None
self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show()
statsFrame = gtk.Frame("Stats:")
statsFrame.set_label_align(0.0, 0.0)
statsFrame.show()
self.stats_frame = gtk.VBox(False, 0)
self.stats_frame.show()
# This could be stored in config eventually, or maybe configured in this window somehow.
# Each posncols element is the name of a column returned by the sql
# query (in lower case) and each posnheads element is the text to use as
# the heading in the GUI. Both sequences should be the same length.
# To miss columns out remove them from both tuples (the 1st 2 elements should always be included).
# To change the heading just edit the second list element as required
# If the first list element does not match a query column that pair is ignored
self.posncols = ( "game", "avgseats", "plposition", "vpip", "pfr", "pf3", "steals"
, "saw_f", "sawsd", "wtsdwsf", "wmsd", "flafq", "tuafq", "rvafq"
, "pofafq", "net", "bbper100", "profitperhand", "variance", "n"
)
self.posnheads = ( "Game", "Seats", "Posn", "VPIP", "PFR", "PF3", "Steals"
, "Saw_F", "SawSD", "WtSDwsF", "W$SD", "FlAFq", "TuAFq", "RvAFq"
, "PoFAFq", "Net($)", "BB/100", "$/hand", "Variance", "Hds"
)
self.fillStatsFrame(self.stats_frame)
statsFrame.add(self.stats_frame)
self.main_hbox.pack_start(self.filters.get_vbox())
self.main_hbox.pack_start(statsFrame)
def get_vbox(self): def get_vbox(self):
"""returns the vbox of this thread""" """returns the vbox of this thread"""
return self.main_hbox return self.main_hbox
@ -35,177 +106,255 @@ class GuiPositionalStats (threading.Thread):
self.activesite = data self.activesite = data
print "DEBUG: activesite set to %s" %(self.activesite) print "DEBUG: activesite set to %s" %(self.activesite)
def cardCallback(self, widget, data=None):
print "DEBUG: %s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
def refreshStats(self, widget, data): def refreshStats(self, widget, data):
try: self.stats_table.destroy() try: self.stats_table.destroy()
except AttributeError: pass except AttributeError: pass
self.fillStatsFrame(self.stats_frame) self.fillStatsFrame(self.stats_frame)
def fillStatsFrame(self, vbox): def fillStatsFrame(self, vbox):
# Get currently active site and grab playerid sites = self.filters.getSites()
print "DEBUG: attempting to fill stats frame" heroes = self.filters.getHeroes()
siteids = self.filters.getSiteIds()
limits = self.filters.getLimits()
seats = self.filters.getSeats()
dates = self.filters.getDates()
sitenos = []
playerids = []
# Which sites are selected?
for site in sites:
if sites[site] == True:
sitenos.append(siteids[site])
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
result = self.db.cursor.fetchall()
if len(result) == 1:
playerids.append(result[0][0])
if not sitenos:
#Should probably pop up here.
print "No sites selected - defaulting to PokerStars"
sitenos = [2]
if not playerids:
print "No player ids found"
return
if not limits:
print "No limits found"
return
self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates)
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates):
self.stats_table = gtk.Table(1, 1, False) # gtk table expands as required
self.stats_table.set_col_spacings(4)
self.stats_table.show()
vbox.add(self.stats_table)
row = 0
col = 0
for t in self.posnheads:
l = gtk.Label(self.posnheads[col])
l.show()
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
col +=1
tmp = self.sql.query['playerStatsByPosition'] tmp = self.sql.query['playerStatsByPosition']
tmp = self.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
result = self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[self.activesite],)) self.cursor.execute(tmp)
result = self.cursor.fetchall() result = self.cursor.fetchall()
if not result == ():
pid = result[0][0]
pid = result[0][0]
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
self.cursor.execute(tmp)
result = self.cursor.fetchall()
cols = 16
rows = len(result)+1 # +1 for title row
self.stats_table = gtk.Table(rows, cols, False)
self.stats_table.set_col_spacings(4)
self.stats_table.show()
vbox.add(self.stats_table)
# Create header row rows = len(result)
titles = ("Game", "Position", "#", "VPIP", "PFR", "Saw_F", "SawSD", "WtSDwsF", "W$SD", "FlAFq", "TuAFq", "RvAFq", "PoFAFq", "Net($)", "BB/100", "$/hand", "Variance") colnames = [desc[0].lower() for desc in self.cursor.description]
col = 0 last_game,last_seats,sqlrow = "","",0
row = 0 while sqlrow < rows:
for t in titles: if(row%2 == 0):
l = gtk.Label(titles[col]) bgcolor = "white"
l.show() else:
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK) bgcolor = "lightgrey"
col +=1 rowprinted=0
avgcol = colnames.index('avgseats')
for row in range(rows-1): for col,colname in enumerate(self.posncols):
if(row%2 == 0): if colname in colnames:
bgcolor = "white" sqlcol = colnames.index(colname)
else:
continue
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
# print blank row between levels:
if result[sqlrow][sqlcol]:
if sqlrow == 0:
l = gtk.Label(result[sqlrow][sqlcol])
rowprinted=1
elif result[sqlrow][0] != last_game:
l = gtk.Label(' ')
elif 'show' in seats and seats['show'] and result[sqlrow][avgcol] != last_seats:
l = gtk.Label(' ')
else: else:
bgcolor = "lightgrey" l = gtk.Label(result[sqlrow][sqlcol])
for col in range(cols): rowprinted=1
eb = gtk.EventBox() else:
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor)) l = gtk.Label(' ')
if result[row][col]: if col == 0:
l = gtk.Label(result[row][col]) l.set_alignment(xalign=0.0, yalign=0.5)
else: elif col == 1:
l = gtk.Label(' ') l.set_alignment(xalign=0.5, yalign=0.5)
if col == 0: else:
l.set_alignment(xalign=0.0, yalign=0.5) l.set_alignment(xalign=1.0, yalign=0.5)
else: eb.add(l)
l.set_alignment(xalign=1.0, yalign=0.5) self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
eb.add(l) l.show()
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK) eb.show()
l.show() last_game = result[sqlrow][0]
eb.show() last_seats = result[sqlrow][avgcol]
self.fdb.db.commit() if rowprinted:
sqlrow = sqlrow+1
row = row + 1
# show totals at bottom
tmp = self.sql.query['playerStats']
tmp = self.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
self.cursor.execute(tmp)
result = self.cursor.fetchall()
rows = len(result)
colnames = [desc[0].lower() for desc in self.cursor.description]
# blank row between main stats and totals:
col = 0
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
l = gtk.Label(' ')
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
l.show()
eb.show()
row = row + 1
for sqlrow in range(rows):
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
for col,colname in enumerate(self.posncols):
if colname in colnames:
sqlcol = colnames.index(colname)
elif colname != "plposition":
continue
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
if colname == 'plposition':
l = gtk.Label('Totals')
elif result[sqlrow][sqlcol]:
l = gtk.Label(result[sqlrow][sqlcol])
else:
l = gtk.Label(' ')
if col == 0:
l.set_alignment(xalign=0.0, yalign=0.5)
elif col == 1:
l.set_alignment(xalign=0.5, yalign=0.5)
else:
l.set_alignment(xalign=1.0, yalign=0.5)
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
l.show()
eb.show()
row = row + 1
self.db.db.rollback()
#end def fillStatsFrame(self, vbox): #end def fillStatsFrame(self, vbox):
def fillPlayerFrame(self, vbox): def refineQuery(self, query, playerids, sitenos, limits, seats, dates):
for site in self.conf.supported_sites.keys(): if playerids:
hbox = gtk.HBox(False, 0) nametest = str(tuple(playerids))
vbox.pack_start(hbox, False, True, 0) nametest = nametest.replace("L", "")
hbox.show() nametest = nametest.replace(",)",")")
query = query.replace("<player_test>", nametest)
player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(hbox, site, player)
hbox = gtk.HBox(False, 0)
button = gtk.Button("Refresh")
button.connect("clicked", self.refreshStats, False)
button.show()
hbox.add(button)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
def fillCardsFrame(self, vbox):
hbox1 = gtk.HBox(True,0)
hbox1.show()
vbox.pack_start(hbox1, True, True, 0)
cards = [ "A", "K","Q","J","T","9","8","7","6","5","4","3","2" ]
for j in range(0, len(cards)):
hbox1 = gtk.HBox(True,0)
hbox1.show()
vbox.pack_start(hbox1, True, True, 0)
for i in range(0, len(cards)):
if i < (j + 1):
suit = "o"
else:
suit = "s"
button = gtk.ToggleButton("%s%s%s" %(cards[i], cards[j], suit))
button.connect("toggled", self.cardCallback, "%s%s%s" %(cards[i], cards[j], suit))
hbox1.pack_start(button, True, True, 0)
button.show()
def createPlayerLine(self, hbox, site, player):
if(self.buttongroup == None):
button = gtk.RadioButton(None, site + " id:")
button.set_active(True)
self.buttongroup = button
self.activesite = site
else: else:
button = gtk.RadioButton(self.buttongroup, site + " id:") query = query.replace("<player_test>", "1 = 2")
hbox.pack_start(button, True, True, 0)
button.connect("toggled", self.toggleCallback, site)
button.show()
pname = gtk.Entry() if seats:
pname.set_text(player) query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
pname.set_width_chars(20) if 'show' in seats and seats['show']:
hbox.pack_start(pname, False, True, 0) query = query.replace('<groupbyseats>', ',hc.activeSeats')
pname.connect("changed", self.__set_hero_name, site) query = query.replace('<orderbyseats>', ',stats.AvgSeats')
#TODO: Look at GtkCompletion - to fill out usernames else:
pname.show() query = query.replace('<groupbyseats>', '')
self.__set_hero_name(pname, site) query = query.replace('<orderbyseats>', '')
else:
query = query.replace('<seats_test>', 'between 0 and 100')
query = query.replace('<groupbyseats>', '')
query = query.replace('<orderbyseats>', '')
def __set_hero_name(self, w, site): if [x for x in limits if str(x).isdigit()]:
self.heroes[site] = w.get_text() blindtest = str(tuple([x for x in limits if str(x).isdigit()]))
blindtest = blindtest.replace("L", "")
blindtest = blindtest.replace(",)",")")
query = query.replace("<gtbigBlind_test>", "gt.bigBlind in " + blindtest)
else:
query = query.replace("<gtbigBlind_test>", "gt.bigBlind = -1 ")
def __init__(self, db, config, querylist, debug=True): groupLevels = "show" not in str(limits)
self.debug=debug if groupLevels:
self.conf=config if self.db.backend == self.MYSQL_INNODB:
bigblindselect = """concat('$'
,trim(leading ' ' from
case when min(gt.bigBlind) < 100
then format(min(gt.bigBlind)/100.0, 2)
else format(min(gt.bigBlind)/100.0, 0)
end)
,' - $'
,trim(leading ' ' from
case when max(gt.bigBlind) < 100
then format(max(gt.bigBlind)/100.0, 2)
else format(max(gt.bigBlind)/100.0, 0)
end)
) """
else:
bigblindselect = """'$' ||
trim(leading ' ' from
case when min(gt.bigBlind) < 100
then to_char(min(gt.bigBlind)/100.0,'90D00')
else to_char(min(gt.bigBlind)/100.0,'999990')
end)
|| ' - $' ||
trim(leading ' ' from
case when max(gt.bigBlind) < 100
then to_char(max(gt.bigBlind)/100.0,'90D00')
else to_char(max(gt.bigBlind)/100.0,'999990')
end) """
bigblindselect = "cast('' as char)" # avoid odd effects when some posns and/or seats
# are missing from some limits (dunno why cast is
# needed but it says "unknown type" otherwise?!
query = query.replace("<selectgt.bigBlind>", bigblindselect)
query = query.replace("<groupbygt.bigBlind>", "")
query = query.replace("<hcgametypeId>", "-1")
query = query.replace("<hgameTypeId>", "-1")
else:
if self.db.backend == self.MYSQL_INNODB:
bigblindselect = """concat('$', trim(leading ' ' from
case when gt.bigBlind < 100
then format(gt.bigBlind/100.0, 2)
else format(gt.bigBlind/100.0, 0)
end
) )"""
else:
bigblindselect = """'$' || trim(leading ' ' from
case when gt.bigBlind < 100
then to_char(gt.bigBlind/100.0,'90D00')
else to_char(gt.bigBlind/100.0,'999990')
end
) """
query = query.replace("<selectgt.bigBlind>", bigblindselect)
query = query.replace("<groupbygt.bigBlind>", ",gt.bigBlind")
query = query.replace("<hcgametypeId>", "hc.gametypeId")
query = query.replace("<hgameTypeId>", "h.gameTypeId")
# create new db connection to avoid conflicts with other threads # Filter on dates
self.fdb = fpdb_db.fpdb_db() query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
self.fdb.do_connect(self.conf)
self.cursor=self.fdb.cursor
self.sql = querylist
self.activesite = None
self.buttongroup = None
self.heroes = {}
self.stat_table = None
self.stats_frame = None
self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show()
playerFrame = gtk.Frame("Hero:")
playerFrame.set_label_align(0.0, 0.0)
playerFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillPlayerFrame(vbox)
playerFrame.add(vbox)
cardsFrame = gtk.Frame("Cards:")
cardsFrame.set_label_align(0.0, 0.0)
cardsFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillCardsFrame(vbox)
cardsFrame.add(vbox)
statsFrame = gtk.Frame("Stats:")
statsFrame.set_label_align(0.0, 0.0)
statsFrame.show()
self.stats_frame = gtk.VBox(False, 0)
self.stats_frame.show()
self.fillStatsFrame(self.stats_frame)
statsFrame.add(self.stats_frame)
self.main_hbox.pack_start(playerFrame)
self.main_hbox.pack_start(statsFrame)
#print "query =\n", query
return(query)
#end def refineQuery(self, query, playerids, sitenos, limits):

310
pyfpdb/GuiSessionViewer.py Normal file
View File

@ -0,0 +1,310 @@
#!/usr/bin/python
#Copyright 2008 Steffen Jobbagy-Felso
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
import threading
import pygtk
pygtk.require('2.0')
import gtk
import os
from time import time, strftime, localtime
from numpy import diff, nonzero
import Card
import fpdb_import
import fpdb_db
import Filters
import FpdbSQLQueries
class GuiSessionViewer (threading.Thread):
def __init__(self, config, querylist, debug=True):
self.debug=debug
self.conf=config
self.MYSQL_INNODB = 2
self.PGSQL = 3
self.SQLITE = 4
# create new db connection to avoid conflicts with other threads
self.db = fpdb_db.fpdb_db()
self.db.do_connect(self.conf)
self.cursor=self.db.cursor
self.sql = querylist
settings = {}
settings.update(config.get_db_parameters())
settings.update(config.get_tv_parameters())
settings.update(config.get_import_parameters())
settings.update(config.get_default_paths())
# text used on screen stored here so that it can be configured
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
}
filters_display = { "Heroes" : True,
"Sites" : True,
"Games" : False,
"Limits" : True,
"LimitSep" : True,
"Seats" : True,
"SeatSep" : True,
"Dates" : False,
"Groups" : True,
"Button1" : True,
"Button2" : True
}
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
self.filters.registerButton2Name("_Refresh")
self.filters.registerButton2Callback(self.refreshStats)
# ToDo: store in config
# ToDo: create popup to adjust column config
# columns to display, keys match column name returned by sql, values in tuple are:
# is column displayed, column heading, xalignment, formatting
self.columns = [ ("game", True, "Game", 0.0, "%s")
, ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line
, ("n", True, "Hds", 1.0, "%d")
, ("avgseats", True, "Seats", 1.0, "%3.1f")
, ("vpip", True, "VPIP", 1.0, "%3.1f")
, ("pfr", True, "PFR", 1.0, "%3.1f")
, ("pf3", True, "PF3", 1.0, "%3.1f")
, ("steals", True, "Steals", 1.0, "%3.1f")
, ("saw_f", True, "Saw_F", 1.0, "%3.1f")
, ("sawsd", True, "SawSD", 1.0, "%3.1f")
, ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f")
, ("wmsd", True, "W$SD", 1.0, "%3.1f")
, ("flafq", True, "FlAFq", 1.0, "%3.1f")
, ("tuafq", True, "TuAFq", 1.0, "%3.1f")
, ("rvafq", True, "RvAFq", 1.0, "%3.1f")
, ("pofafq", False, "PoFAFq", 1.0, "%3.1f")
, ("net", True, "Net($)", 1.0, "%6.2f")
, ("bbper100", True, "BB/100", 1.0, "%4.2f")
, ("rake", True, "Rake($)", 1.0, "%6.2f")
, ("variance", True, "Variance", 1.0, "%5.2f")
]
self.stats_frame = None
self.stats_vbox = None
self.detailFilters = [] # the data used to enhance the sql select
self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show()
self.stats_frame = gtk.Frame()
self.stats_frame.show()
self.stats_vbox = gtk.VBox(False, 0)
self.stats_vbox.show()
self.stats_frame.add(self.stats_vbox)
self.fillStatsFrame(self.stats_vbox)
self.main_hbox.pack_start(self.filters.get_vbox())
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
################################
# make sure Hand column is not displayed
[x for x in self.columns if x[0] == 'hand'][0][1] == False
def get_vbox(self):
"""returns the vbox of this thread"""
return self.main_hbox
def refreshStats(self, widget, data):
try: self.stats_vbox.destroy()
except AttributeError: pass
self.stats_vbox = gtk.VBox(False, 0)
self.stats_vbox.show()
self.stats_frame.add(self.stats_vbox)
self.fillStatsFrame(self.stats_vbox)
def fillStatsFrame(self, vbox):
sites = self.filters.getSites()
heroes = self.filters.getHeroes()
siteids = self.filters.getSiteIds()
limits = self.filters.getLimits()
seats = self.filters.getSeats()
sitenos = []
playerids = []
# Which sites are selected?
for site in sites:
if sites[site] == True:
sitenos.append(siteids[site])
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
result = self.db.cursor.fetchall()
if len(result) == 1:
playerids.append(result[0][0])
if not sitenos:
#Should probably pop up here.
print "No sites selected - defaulting to PokerStars"
sitenos = [2]
if not playerids:
print "No player ids found"
return
if not limits:
print "No limits found"
return
self.createStatsTable(vbox, playerids, sitenos, limits, seats)
def createStatsTable(self, vbox, playerids, sitenos, limits, seats):
starttime = time()
# Display summary table at top of page
# 3rd parameter passes extra flags, currently includes:
# holecards - whether to display card breakdown (True/False)
flags = [False]
self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, seats)
# Separator
sep = gtk.HSeparator()
vbox.pack_start(sep, expand=False, padding=3)
sep.show_now()
vbox.show_now()
heading = gtk.Label(self.filterText['handhead'])
heading.show()
vbox.pack_start(heading, expand=False, padding=3)
# Scrolled window for detailed table (display by hand)
swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)
swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
swin.show()
vbox.pack_start(swin, expand=True, padding=3)
vbox1 = gtk.VBox(False, 0)
vbox1.show()
swin.add_with_viewport(vbox1)
# Detailed table
flags = [True]
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats)
self.db.db.commit()
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
#end def fillStatsFrame(self, vbox):
def addTable(self, vbox, query, flags, playerids, sitenos, limits, seats):
row = 0
sqlrow = 0
colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4
if not flags: holecards = False
else: holecards = flags[0]
self.stats_table = gtk.Table(1, 1, False)
self.stats_table.set_col_spacings(4)
self.stats_table.show()
self.db.cursor.execute("""select UNIX_TIMESTAMP(handStart) as time, id from Hands ORDER BY time""")
THRESHOLD = 1800
hands = self.db.cursor.fetchall()
times = map(lambda x:long(x[0]), hands)
handids = map(lambda x:int(x[1]), hands)
print "DEBUG: len(times) %s" %(len(times))
diffs = diff(times)
print "DEBUG: len(diffs) %s" %(len(diffs))
index = nonzero(diff(times) > THRESHOLD)
print "DEBUG: len(index[0]) %s" %(len(index[0]))
print "DEBUG: index %s" %(index)
print "DEBUG: index[0][0] %s" %(index[0][0])
total = 0
last_idx = 0
for i in range(len(index[0])):
print "Hands in session %4s: %4s Start: %s End: %s Total: %s" %(i, index[0][i] - last_idx, strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])), strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])), times[index[0][i]] - times[last_idx])
total = total + (index[0][i] - last_idx)
last_idx = index[0][i] + 1
print "Total: ", total
#
# colnames = [desc[0].lower() for desc in self.cursor.description]
#
# # pre-fetch some constant values:
# cols_to_show = [x for x in self.columns if x[colshow]]
# hgametypeid_idx = colnames.index('hgametypeid')
#
# liststore = gtk.ListStore(*([str] * len(cols_to_show)))
# view = gtk.TreeView(model=liststore)
# view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
# vbox.pack_start(view, expand=False, padding=3)
# textcell = gtk.CellRendererText()
# numcell = gtk.CellRendererText()
# numcell.set_property('xalign', 1.0)
# listcols = []
#
# # Create header row eg column: ("game", True, "Game", 0.0, "%s")
# for col, column in enumerate(cols_to_show):
# if column[colalias] == 'game' and holecards:
# s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading]
# else:
# s = column[colheading]
# listcols.append(gtk.TreeViewColumn(s))
# view.append_column(listcols[col])
# if column[colformat] == '%s':
# if col == 1 and holecards:
# listcols[col].pack_start(textcell, expand=True)
# else:
# listcols[col].pack_start(textcell, expand=False)
# listcols[col].add_attribute(textcell, 'text', col)
# else:
# listcols[col].pack_start(numcell, expand=False)
# listcols[col].add_attribute(numcell, 'text', col)
#
# rows = len(result) # +1 for title row
#
# while sqlrow < rows:
# treerow = []
# if(row%2 == 0):
# bgcolor = "white"
# else:
# bgcolor = "lightgrey"
# for col,column in enumerate(cols_to_show):
# if column[colalias] in colnames:
# value = result[sqlrow][colnames.index(column[colalias])]
# else:
# if column[colalias] == 'game':
# if holecards:
# value = Card.twoStartCardString( result[sqlrow][hgametypeid_idx] )
# else:
# minbb = result[sqlrow][colnames.index('minbigblind')]
# maxbb = result[sqlrow][colnames.index('maxbigblind')]
# value = result[sqlrow][colnames.index('limittype')] + ' ' \
# + result[sqlrow][colnames.index('category')].title() + ' ' \
# + result[sqlrow][colnames.index('name')] + ' $'
# if 100 * int(minbb/100.0) != minbb:
# value += '%.2f' % (minbb/100.0)
# else:
# value += '%.0f' % (minbb/100.0)
# if minbb != maxbb:
# if 100 * int(maxbb/100.0) != maxbb:
# value += ' - $' + '%.2f' % (maxbb/100.0)
# else:
# value += ' - $' + '%.0f' % (maxbb/100.0)
# else:
# continue
# if value and value != -999:
# treerow.append(column[colformat] % value)
# else:
# treerow.append(' ')
# iter = liststore.append(treerow)
# sqlrow += 1
# row += 1
vbox.show_all()

View File

@ -76,7 +76,7 @@ class GuiTableViewer (threading.Thread):
arr=[] arr=[]
#first prepare the header row #first prepare the header row
if (self.category=="holdem" or self.category=="omahahi" or self.category=="omahahilo"): if (self.category=="holdem" or self.category=="omahahi" or self.category=="omahahilo"):
tmp=("Name", "HDs", "VPIP", "PFR", "PF3B4B", "ST") tmp=("Name", "HDs", "VPIP", "PFR", "PF3B", "ST")
tmp+=("FS", "FB") tmp+=("FS", "FB")
@ -131,7 +131,7 @@ class GuiTableViewer (threading.Thread):
tmp.append(str(row[6]))#Hands tmp.append(str(row[6]))#Hands
tmp.append(self.hudDivide(row[7],row[6])) #VPIP tmp.append(self.hudDivide(row[7],row[6])) #VPIP
tmp.append(self.hudDivide(row[8],row[6])) #PFR tmp.append(self.hudDivide(row[8],row[6])) #PFR
tmp.append(self.hudDivide(row[10],row[9])+" ("+str(row[9])+")") #PF3B4B tmp.append(self.hudDivide(row[10],row[9])+" ("+str(row[9])+")") #PF3B
tmp.append(self.hudDivide(row[31],row[30])+" ("+str(row[30])+")") #ST tmp.append(self.hudDivide(row[31],row[30])+" ("+str(row[30])+")") #ST

View File

@ -1,8 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FreePokerToolsConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FreePokerToolsConfig.xsd"> <FreePokerToolsConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FreePokerToolsConfig.xsd">
<import callFpdbHud = "True" interval = "10" fastStoreHudCache="False" hhArchiveBase="~/.fpdb/HandHistories/" saveActions="True"></import>
<supported_sites> <supported_sites>
<site enabled="True" site_name="PokerStars" table_finder="PokerStars.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/PokerStars/" HH_path="~/.wine/drive_c/Program Files/PokerStars/HandHistory/abc/" decoder="pokerstars_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<site enabled="True"
site_name="PokerStars"
table_finder="PokerStars.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path="C:/Program Files/PokerStars/"
HH_path="C:/Program Files/PokerStars/HandHistory/YOUR SCREEN NAME HERE/"
decoder="pokerstars_decode_table"
converter="PokerStarsToFpdb"
bgcolor="#000000"
fgcolor="#FFFFFF"
hudopacity="1.0"
font="Sans"
font_size="8"
supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<layout max="8" width="792" height="546" fav_seat="0"> <layout max="8" width="792" height="546" fav_seat="0">
<location seat="1" x="684" y="61"> </location> <location seat="1" x="684" y="61"> </location>
<location seat="2" x="689" y="239"> </location> <location seat="2" x="689" y="239"> </location>
@ -49,7 +66,21 @@
<location seat="2" x="10" y="288"> </location> <location seat="2" x="10" y="288"> </location>
</layout> </layout>
</site> </site>
<site enabled="True" site_name="Full Tilt Poker" table_finder="FullTiltPoker.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<site enabled="True"
site_name="Full Tilt Poker"
table_finder="FullTiltPoker"
screen_name="YOUR SCREEN NAME HERE"
site_path="C:/Program Files/Full Tilt Poker/"
HH_path="C:/Program Files/Full Tilt Poker/HandHistory/YOUR SCREEN NAME HERE/"
decoder="fulltilt_decode_table"
converter="FulltiltToFpdb"
bgcolor="#000000"
fgcolor="#FFFFFF"
hudopacity="1.0"
font="Sans"
font_size="8"
supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<layout fav_seat="0" height="547" max="8" width="794"> <layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location> <location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location> <location seat="2" x="650" y="230"> </location>
@ -84,7 +115,16 @@
<location seat="9" x="70" y="53"> </location> <location seat="9" x="70" y="53"> </location>
</layout> </layout>
</site> </site>
<site enabled="False" site_name="Everleaf" table_finder="Everleaf.exe" screen_name="DO NOT NEED THIS YET" site_path="" HH_path="" decoder="everleaf_decode_table" converter="EverleafToFpdb" supported_games="holdem">
<site enabled="False"
site_name="Everleaf"
table_finder="Everleaf.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path=""
HH_path=""
decoder="everleaf_decode_table"
converter="EverleafToFpdb"
supported_games="holdem">
<layout fav_seat="0" height="547" max="8" width="794"> <layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location> <location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location> <location seat="2" x="650" y="230"> </location>
@ -120,8 +160,10 @@
</layout> </layout>
</site> </site>
</supported_sites> </supported_sites>
<supported_games> <supported_games>
<game cols="3" db="fpdb" game_name="holdem" rows="2">
<game cols="3" db="fpdb" game_name="holdem" rows="2" aux="mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -129,7 +171,8 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="razz" rows="2">
<game cols="3" db="fpdb" game_name="razz" rows="2" aux="stud_mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -137,7 +180,8 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="omahahi" rows="2">
<game cols="3" db="fpdb" game_name="omahahi" rows="2" aux="mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -145,7 +189,8 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="omahahilo" rows="2">
<game cols="3" db="fpdb" game_name="omahahilo" rows="2" aux="mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -153,7 +198,8 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="studhi" rows="2">
<game cols="3" db="fpdb" game_name="studhi" rows="2" aux="stud_mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -161,7 +207,8 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="studhilo" rows="2">
<game cols="3" db="fpdb" game_name="studhilo" rows="2" aux="stud_mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -170,6 +217,7 @@
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
</supported_games> </supported_games>
<popup_windows> <popup_windows>
<pu pu_name="default"> <pu pu_name="default">
<pu_stat pu_stat_name="n"> </pu_stat> <pu_stat pu_stat_name="n"> </pu_stat>
@ -196,15 +244,60 @@
<pu_stat pu_stat_name="ffreq_4"> </pu_stat> <pu_stat pu_stat_name="ffreq_4"> </pu_stat>
</pu> </pu>
</popup_windows> </popup_windows>
<import callFpdbHud = "True" interval = "10" ></import>
<tv combinedStealFold = "True" combined2B3B = "True" combinedPostflop = "True"></tv> <aux_windows>
<aw card_ht="42" card_wd="30" class="Stud_mucked" cols="11" deck="Cards01.png" module="Mucked" name="stud_mucked" rows="8"> </aw>
<aw class="Hello" module="Hello" name="Hello"> </aw>
<aw class="Hello_Menu" module="Hello" name="Hello_menu"> </aw>
<aw class="Hello_plus" module="Hello" name="Hello_plus"> </aw>
<aw card_ht="42" card_wd="30" class="Flop_Mucked" deck="Cards01.png" module="Mucked" name="mucked" opacity="0.7" timeout="5">
<layout height="546" max="6" width="792">
<location seat="1" x="555" y="169"> </location>
<location seat="2" x="572" y="276"> </location>
<location seat="3" x="363" y="348"> </location>
<location seat="4" x="150" y="273"> </location>
<location seat="5" x="150" y="169"> </location>
<location seat="6" x="363" y="113"> </location>
<location common="1" x="323" y="232"> </location>
</layout>
<layout height="546" max="9" width="792">
<location seat="1" x="486" y="113"> </location>
<location seat="2" x="555" y="169"> </location>
<location seat="3" x="572" y="276"> </location>
<location seat="4" x="522" y="345"> </location>
<location seat="5" x="363" y="348"> </location>
<location seat="6" x="217" y="341"> </location>
<location seat="7" x="150" y="273"> </location>
<location seat="8" x="150" y="169"> </location>
<location seat="9" x="230" y="115"> </location>
<location common="1" x="323" y="232"> </location>
</layout>
<layout height="546" max="10" width="792">
<location seat="1" x="486" y="113"> </location>
<location seat="2" x="499" y="138"> </location>
<location seat="3" x="522" y="212"> </location>
<location seat="4" x="501" y="281"> </location>
<location seat="5" x="402" y="323"> </location>
<location seat="6" x="243" y="311"> </location>
<location seat="7" x="203" y="262"> </location>
<location seat="8" x="170" y="185"> </location>
<location seat="9" x="183" y="128"> </location>
<location seat="10" x="213" y="86"> </location>
<location common="1" x="317" y="237"> </location>
</layout>
</aw>
</aux_windows>
<hhcs>
<hhc site="PokerStars" converter="PokerStarsToFpdb"/>
<hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/>
<hhc site="Everleaf" converter="EverleafToFpdb"/>
</hhcs>
<supported_databases> <supported_databases>
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"> </database> <database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"></database>
</supported_databases> </supported_databases>
<mucked_windows>
<mw mw_name="stud1" format="stud" rows="8" cols="11" deck="Cards01.png" card_wd="30" card_ht="42"> </mw>
</mucked_windows>
</FreePokerToolsConfig> </FreePokerToolsConfig>

View File

@ -51,6 +51,8 @@ import Database
import Tables import Tables
import Hud import Hud
aggregate_stats = {"ring": False, "tour": False} # config file!
class HUD_main(object): class HUD_main(object):
"""A main() object to own both the read_stdin thread and the gui.""" """A main() object to own both the read_stdin thread and the gui."""
# This class mainly provides state for controlling the multiple HUDs. # This class mainly provides state for controlling the multiple HUDs.
@ -79,13 +81,14 @@ class HUD_main(object):
def kill_hud(self, event, table): def kill_hud(self, event, table):
# called by an event in the HUD, to kill this specific HUD # called by an event in the HUD, to kill this specific HUD
self.hud_dict[table].kill() if table in self.hud_dict:
self.hud_dict[table].main_window.destroy() self.hud_dict[table].kill()
self.vb.remove(self.hud_dict[table].tablehudlabel) self.hud_dict[table].main_window.destroy()
del(self.hud_dict[table]) self.vb.remove(self.hud_dict[table].tablehudlabel)
del(self.hud_dict[table])
self.main_window.resize(1,1) self.main_window.resize(1,1)
def create_HUD(self, new_hand_id, table, table_name, max, poker_game, is_tournament, stat_dict, cards): def create_HUD(self, new_hand_id, table, table_name, max, poker_game, stat_dict, cards):
def idle_func(): def idle_func():
@ -108,9 +111,9 @@ class HUD_main(object):
gtk.gdk.threads_leave() gtk.gdk.threads_leave()
self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection) self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection)
self.hud_dict[table_name].table_name = table_name
self.hud_dict[table_name].stat_dict = stat_dict self.hud_dict[table_name].stat_dict = stat_dict
self.hud_dict[table_name].cards = cards self.hud_dict[table_name].cards = cards
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows] [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows]
gobject.idle_add(idle_func) gobject.idle_add(idle_func)
@ -149,8 +152,8 @@ class HUD_main(object):
# get basic info about the new hand from the db # get basic info about the new hand from the db
# if there is a db error, complain, skip hand, and proceed # if there is a db error, complain, skip hand, and proceed
try: try:
(table_name, max, poker_game) = self.db_connection.get_table_name(new_hand_id) (table_name, max, poker_game, type) = self.db_connection.get_table_name(new_hand_id)
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id) stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate = aggregate_stats[type])
cards = self.db_connection.get_cards(new_hand_id) cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id) comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud! if comm_cards != {}: # stud!
@ -160,15 +163,17 @@ class HUD_main(object):
sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id))) sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
continue continue
# find out if this hand is from a tournament if type == "tour": # hand is from a tournament
mat_obj = tourny_finder.search(table_name) mat_obj = tourny_finder.search(table_name)
if mat_obj: if mat_obj:
is_tournament = True (tour_number, tab_number) = mat_obj.group(1, 2)
(tour_number, tab_number) = mat_obj.group(1, 2) temp_key = tour_number
temp_key = tour_number else: # tourney, but can't get number and table
print "could not find tournament: skipping "
sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
continue
else: else:
is_tournament = False
(tour_number, tab_number) = (0, 0)
temp_key = table_name temp_key = table_name
# Update an existing HUD # Update an existing HUD
@ -180,18 +185,18 @@ class HUD_main(object):
# Or create a new HUD # Or create a new HUD
else: else:
if is_tournament: if type == "tour":
tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number) tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
else: else:
tablewindow = Tables.discover_table_by_name(self.config, table_name) tablewindow = Tables.discover_table_by_name(self.config, table_name)
if tablewindow == None: if tablewindow == None:
# If no client window is found on the screen, complain and continue # If no client window is found on the screen, complain and continue
if is_tournament: if type == "tour":
table_name = "%s %s" % (tour_number, tab_number) table_name = "%s %s" % (tour_number, tab_number)
sys.stderr.write("table name "+table_name+" not found, skipping.\n") sys.stderr.write("table name "+table_name+" not found, skipping.\n")
else: else:
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, is_tournament, stat_dict, cards) self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, stat_dict, cards)
self.db_connection.connection.rollback()
if __name__== "__main__": if __name__== "__main__":
sys.stderr.write("HUD_main starting\n") sys.stderr.write("HUD_main starting\n")

View File

@ -125,7 +125,7 @@ class Hud:
self.menu = gtk.Menu() self.menu = gtk.Menu()
self.item1 = gtk.MenuItem('Kill this HUD') self.item1 = gtk.MenuItem('Kill this HUD')
self.menu.append(self.item1) self.menu.append(self.item1)
self.item1.connect("activate", self.parent.kill_hud, self.table.name) self.item1.connect("activate", self.parent.kill_hud, self.table_name)
self.item1.show() self.item1.show()
self.item2 = gtk.MenuItem('Save Layout') self.item2 = gtk.MenuItem('Save Layout')
@ -204,14 +204,14 @@ class Hud:
def reposition_windows(self, *args): def reposition_windows(self, *args):
for w in self.stat_windows.itervalues(): for w in self.stat_windows.itervalues():
if type(w) == int: if type(w) == int:
print "in reposition, w =", w # print "in reposition, w =", w
continue continue
print "in reposition, w =", w, w.x, w.y # print "in reposition, w =", w, w.x, w.y
w.window.move(w.x, w.y) w.window.move(w.x, w.y)
return True return True
def debug_stat_windows(self, *args): def debug_stat_windows(self, *args):
print self.table, "\n", self.main_window.window.get_transient_for() # print self.table, "\n", self.main_window.window.get_transient_for()
for w in self.stat_windows: for w in self.stat_windows:
print self.stat_windows[w].window.window.get_transient_for() print self.stat_windows[w].window.window.get_transient_for()

View File

@ -451,7 +451,7 @@ class Flop_Mucked(Aux_Window):
def save_layout(self, *args): def save_layout(self, *args):
"""Save new layout back to the aux element in the config file.""" """Save new layout back to the aux element in the config file."""
new_locs = {} new_locs = {}
print "adj =", self.adj # print "adj =", self.adj
for (i, pos) in self.positions.iteritems(): for (i, pos) in self.positions.iteritems():
if i != 'common': if i != 'common':
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y) new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)

View File

@ -73,7 +73,7 @@ follow : whether to tail -f the input"""
["ring", "hold", "pl"], ["ring", "hold", "pl"],
["ring", "hold", "fl"], ["ring", "hold", "fl"],
["ring", "stud", "fl"], ["ring", "stud", "fl"],
["ring", "draw", "fl"], #["ring", "draw", "fl"],
["ring", "omaha", "pl"] ["ring", "omaha", "pl"]
] ]

View File

@ -29,7 +29,7 @@ Set up all of the SQL statements for a given game and database type.
class Sql: class Sql:
def __init__(self, game = 'holdem', type = 'PT3'): def __init__(self, game = 'holdem', type = 'PT3', db_server = 'mysql'):
self.query = {} self.query = {}
############################################################################ ############################################################################
@ -172,149 +172,329 @@ class Sql:
""" """
self.query['get_stats_from_hand'] = """ self.query['get_stats_from_hand'] = """
SELECT HudCache.playerId AS player_id, SELECT hc.playerId AS player_id,
seatNo AS seat, hp.seatNo AS seat,
name AS screen_name, p.name AS screen_name,
sum(HDs) AS n, sum(hc.HDs) AS n,
sum(street0VPI) AS vpip, sum(hc.street0VPI) AS vpip,
sum(street0Aggr) AS pfr, sum(hc.street0Aggr) AS pfr,
sum(street0_3B4BChance) AS TB_opp_0, sum(hc.street0_3BChance) AS TB_opp_0,
sum(street0_3B4BDone) AS TB_0, sum(hc.street0_3BDone) AS TB_0,
sum(street1Seen) AS saw_f, sum(hc.street1Seen) AS saw_f,
sum(street1Seen) AS saw_1, sum(hc.street1Seen) AS saw_1,
sum(street2Seen) AS saw_2, sum(hc.street2Seen) AS saw_2,
sum(street3Seen) AS saw_3, sum(hc.street3Seen) AS saw_3,
sum(street4Seen) AS saw_4, sum(hc.street4Seen) AS saw_4,
sum(sawShowdown) AS sd, sum(hc.sawShowdown) AS sd,
sum(street1Aggr) AS aggr_1, sum(hc.street1Aggr) AS aggr_1,
sum(street2Aggr) AS aggr_2, sum(hc.street2Aggr) AS aggr_2,
sum(street3Aggr) AS aggr_3, sum(hc.street3Aggr) AS aggr_3,
sum(street4Aggr) AS aggr_4, sum(hc.street4Aggr) AS aggr_4,
sum(otherRaisedStreet1) AS was_raised_1, sum(hc.otherRaisedStreet1) AS was_raised_1,
sum(otherRaisedStreet2) AS was_raised_2, sum(hc.otherRaisedStreet2) AS was_raised_2,
sum(otherRaisedStreet3) AS was_raised_3, sum(hc.otherRaisedStreet3) AS was_raised_3,
sum(otherRaisedStreet4) AS was_raised_4, sum(hc.otherRaisedStreet4) AS was_raised_4,
sum(foldToOtherRaisedStreet1) AS f_freq_1, sum(hc.foldToOtherRaisedStreet1) AS f_freq_1,
sum(foldToOtherRaisedStreet2) AS f_freq_2, sum(hc.foldToOtherRaisedStreet2) AS f_freq_2,
sum(foldToOtherRaisedStreet3) AS f_freq_3, sum(hc.foldToOtherRaisedStreet3) AS f_freq_3,
sum(foldToOtherRaisedStreet4) AS f_freq_4, sum(hc.foldToOtherRaisedStreet4) AS f_freq_4,
sum(wonWhenSeenStreet1) AS w_w_s_1, sum(hc.wonWhenSeenStreet1) AS w_w_s_1,
sum(wonAtSD) AS wmsd, sum(hc.wonAtSD) AS wmsd,
sum(stealAttemptChance) AS steal_opp, sum(hc.stealAttemptChance) AS steal_opp,
sum(stealAttempted) AS steal, sum(hc.stealAttempted) AS steal,
sum(foldSbToStealChance) AS SBstolen, sum(hc.foldSbToStealChance) AS SBstolen,
sum(foldedSbToSteal) AS SBnotDef, sum(hc.foldedSbToSteal) AS SBnotDef,
sum(foldBbToStealChance) AS BBstolen, sum(hc.foldBbToStealChance) AS BBstolen,
sum(foldedBbToSteal) AS BBnotDef, sum(hc.foldedBbToSteal) AS BBnotDef,
sum(street1CBChance) AS CB_opp_1, sum(hc.street1CBChance) AS CB_opp_1,
sum(street1CBDone) AS CB_1, sum(hc.street1CBDone) AS CB_1,
sum(street2CBChance) AS CB_opp_2, sum(hc.street2CBChance) AS CB_opp_2,
sum(street2CBDone) AS CB_2, sum(hc.street2CBDone) AS CB_2,
sum(street3CBChance) AS CB_opp_3, sum(hc.street3CBChance) AS CB_opp_3,
sum(street3CBDone) AS CB_3, sum(hc.street3CBDone) AS CB_3,
sum(street4CBChance) AS CB_opp_4, sum(hc.street4CBChance) AS CB_opp_4,
sum(street4CBDone) AS CB_4, sum(hc.street4CBDone) AS CB_4,
sum(foldToStreet1CBChance) AS f_cb_opp_1, sum(hc.foldToStreet1CBChance) AS f_cb_opp_1,
sum(foldToStreet1CBDone) AS f_cb_1, sum(hc.foldToStreet1CBDone) AS f_cb_1,
sum(foldToStreet2CBChance) AS f_cb_opp_2, sum(hc.foldToStreet2CBChance) AS f_cb_opp_2,
sum(foldToStreet2CBDone) AS f_cb_2, sum(hc.foldToStreet2CBDone) AS f_cb_2,
sum(foldToStreet3CBChance) AS f_cb_opp_3, sum(hc.foldToStreet3CBChance) AS f_cb_opp_3,
sum(foldToStreet3CBDone) AS f_cb_3, sum(hc.foldToStreet3CBDone) AS f_cb_3,
sum(foldToStreet4CBChance) AS f_cb_opp_4, sum(hc.foldToStreet4CBChance) AS f_cb_opp_4,
sum(foldToStreet4CBDone) AS f_cb_4, sum(hc.foldToStreet4CBDone) AS f_cb_4,
sum(totalProfit) AS net, sum(hc.totalProfit) AS net,
sum(street1CheckCallRaiseChance) AS ccr_opp_1, sum(hc.street1CheckCallRaiseChance) AS ccr_opp_1,
sum(street1CheckCallRaiseDone) AS ccr_1, sum(hc.street1CheckCallRaiseDone) AS ccr_1,
sum(street2CheckCallRaiseChance) AS ccr_opp_2, sum(hc.street2CheckCallRaiseChance) AS ccr_opp_2,
sum(street2CheckCallRaiseDone) AS ccr_2, sum(hc.street2CheckCallRaiseDone) AS ccr_2,
sum(street3CheckCallRaiseChance) AS ccr_opp_3, sum(hc.street3CheckCallRaiseChance) AS ccr_opp_3,
sum(street3CheckCallRaiseDone) AS ccr_3, sum(hc.street3CheckCallRaiseDone) AS ccr_3,
sum(street4CheckCallRaiseChance) AS ccr_opp_4, sum(hc.street4CheckCallRaiseChance) AS ccr_opp_4,
sum(street4CheckCallRaiseDone) AS ccr_4 sum(hc.street4CheckCallRaiseDone) AS ccr_4
FROM Hands FROM Hands h
INNER JOIN HandsPlayers ON (HandsPlayers.handId = %s) INNER JOIN HandsPlayers hp ON (hp.handId = %s)
INNER JOIN HudCache ON ( HudCache.PlayerId = HandsPlayers.PlayerId+0 INNER JOIN HudCache hc ON ( hc.PlayerId = hp.PlayerId+0
AND HudCache.gametypeId+0 = Hands.gametypeId+0) AND hc.gametypeId+0 = h.gametypeId+0)
INNER JOIN Players ON (Players.id = HandsPlayers.PlayerId+0) INNER JOIN Players p ON (p.id = hp.PlayerId+0)
WHERE Hands.id = %s WHERE h.id = %s
GROUP BY HudCache.PlayerId AND hc.styleKey > %s
/* styleKey is currently 'd' (for date) followed by a yyyymmdd
date key. Set it to 0000000 or similar to get all records */
/* also check activeseats here? even if only 3 groups eg 2-3/4-6/7+ ??
e.g. could use a multiplier:
AND h.seats > X / 1.25 and hp.seats < X * 1.25
where X is the number of active players at the current table (and
1.25 would be a config value so user could change it)
*/
GROUP BY hc.PlayerId, hp.seatNo, p.name
""" """
# same as above except stats are aggregated for all blind/limit levels # same as above except stats are aggregated for all blind/limit levels
self.query['get_stats_from_hand_aggregated'] = """ self.query['get_stats_from_hand_aggregated'] = """
SELECT HudCache.playerId AS player_id, SELECT hc.playerId AS player_id,
sum(HDs) AS n, max(case when hc.gametypeId = h.gametypeId
sum(street0VPI) AS vpip, then hp.seatNo
sum(street0Aggr) AS pfr, else -1
sum(street0_3B4BChance) AS TB_opp_0, end) AS seat,
sum(street0_3B4BDone) AS TB_0, p.name AS screen_name,
sum(street1Seen) AS saw_f, sum(hc.HDs) AS n,
sum(street1Seen) AS saw_1, sum(hc.street0VPI) AS vpip,
sum(street2Seen) AS saw_2, sum(hc.street0Aggr) AS pfr,
sum(street3Seen) AS saw_3, sum(hc.street0_3BChance) AS TB_opp_0,
sum(street4Seen) AS saw_4, sum(hc.street0_3BDone) AS TB_0,
sum(sawShowdown) AS sd, sum(hc.street1Seen) AS saw_f,
sum(street1Aggr) AS aggr_1, sum(hc.street1Seen) AS saw_1,
sum(street2Aggr) AS aggr_2, sum(hc.street2Seen) AS saw_2,
sum(street3Aggr) AS aggr_3, sum(hc.street3Seen) AS saw_3,
sum(street4Aggr) AS aggr_4, sum(hc.street4Seen) AS saw_4,
sum(otherRaisedStreet1) AS was_raised_1, sum(hc.sawShowdown) AS sd,
sum(otherRaisedStreet2) AS was_raised_2, sum(hc.street1Aggr) AS aggr_1,
sum(otherRaisedStreet3) AS was_raised_3, sum(hc.street2Aggr) AS aggr_2,
sum(otherRaisedStreet4) AS was_raised_4, sum(hc.street3Aggr) AS aggr_3,
sum(foldToOtherRaisedStreet1) AS f_freq_1, sum(hc.street4Aggr) AS aggr_4,
sum(foldToOtherRaisedStreet2) AS f_freq_2, sum(hc.otherRaisedStreet1) AS was_raised_1,
sum(foldToOtherRaisedStreet3) AS f_freq_3, sum(hc.otherRaisedStreet2) AS was_raised_2,
sum(foldToOtherRaisedStreet4) AS f_freq_4, sum(hc.otherRaisedStreet3) AS was_raised_3,
sum(wonWhenSeenStreet1) AS w_w_s_1, sum(hc.otherRaisedStreet4) AS was_raised_4,
sum(wonAtSD) AS wmsd, sum(hc.foldToOtherRaisedStreet1) AS f_freq_1,
sum(stealAttemptChance) AS steal_opp, sum(hc.foldToOtherRaisedStreet2) AS f_freq_2,
sum(stealAttempted) AS steal, sum(hc.foldToOtherRaisedStreet3) AS f_freq_3,
sum(foldSbToStealChance) AS SBstolen, sum(hc.foldToOtherRaisedStreet4) AS f_freq_4,
sum(foldedSbToSteal) AS SBnotDef, sum(hc.wonWhenSeenStreet1) AS w_w_s_1,
sum(foldBbToStealChance) AS BBstolen, sum(hc.wonAtSD) AS wmsd,
sum(foldedBbToSteal) AS BBnotDef, sum(hc.stealAttemptChance) AS steal_opp,
sum(street1CBChance) AS CB_opp_1, sum(hc.stealAttempted) AS steal,
sum(street1CBDone) AS CB_1, sum(hc.foldSbToStealChance) AS SBstolen,
sum(street2CBChance) AS CB_opp_2, sum(hc.foldedSbToSteal) AS SBnotDef,
sum(street2CBDone) AS CB_2, sum(hc.foldBbToStealChance) AS BBstolen,
sum(street3CBChance) AS CB_opp_3, sum(hc.foldedBbToSteal) AS BBnotDef,
sum(street3CBDone) AS CB_3, sum(hc.street1CBChance) AS CB_opp_1,
sum(street4CBChance) AS CB_opp_4, sum(hc.street1CBDone) AS CB_1,
sum(street4CBDone) AS CB_4, sum(hc.street2CBChance) AS CB_opp_2,
sum(foldToStreet1CBChance) AS f_cb_opp_1, sum(hc.street2CBDone) AS CB_2,
sum(foldToStreet1CBDone) AS f_cb_1, sum(hc.street3CBChance) AS CB_opp_3,
sum(foldToStreet2CBChance) AS f_cb_opp_2, sum(hc.street3CBDone) AS CB_3,
sum(foldToStreet2CBDone) AS f_cb_2, sum(hc.street4CBChance) AS CB_opp_4,
sum(foldToStreet3CBChance) AS f_cb_opp_3, sum(hc.street4CBDone) AS CB_4,
sum(foldToStreet3CBDone) AS f_cb_3, sum(hc.foldToStreet1CBChance) AS f_cb_opp_1,
sum(foldToStreet4CBChance) AS f_cb_opp_4, sum(hc.foldToStreet1CBDone) AS f_cb_1,
sum(foldToStreet4CBDone) AS f_cb_4, sum(hc.foldToStreet2CBChance) AS f_cb_opp_2,
sum(totalProfit) AS net, sum(hc.foldToStreet2CBDone) AS f_cb_2,
sum(street1CheckCallRaiseChance) AS ccr_opp_1, sum(hc.foldToStreet3CBChance) AS f_cb_opp_3,
sum(street1CheckCallRaiseDone) AS ccr_1, sum(hc.foldToStreet3CBDone) AS f_cb_3,
sum(street2CheckCallRaiseChance) AS ccr_opp_2, sum(hc.foldToStreet4CBChance) AS f_cb_opp_4,
sum(street2CheckCallRaiseDone) AS ccr_2, sum(hc.foldToStreet4CBDone) AS f_cb_4,
sum(street3CheckCallRaiseChance) AS ccr_opp_3, sum(hc.totalProfit) AS net,
sum(street3CheckCallRaiseDone) AS ccr_3, sum(hc.street1CheckCallRaiseChance) AS ccr_opp_1,
sum(street4CheckCallRaiseChance) AS ccr_opp_4, sum(hc.street1CheckCallRaiseDone) AS ccr_1,
sum(street4CheckCallRaiseDone) AS ccr_4 sum(hc.street2CheckCallRaiseChance) AS ccr_opp_2,
FROM HudCache, Hands sum(hc.street2CheckCallRaiseDone) AS ccr_2,
WHERE HudCache.PlayerId in sum(hc.street3CheckCallRaiseChance) AS ccr_opp_3,
(SELECT PlayerId FROM HandsPlayers sum(hc.street3CheckCallRaiseDone) AS ccr_3,
WHERE handId = %s) sum(hc.street4CheckCallRaiseChance) AS ccr_opp_4,
AND Hands.id = %s sum(hc.street4CheckCallRaiseDone) AS ccr_4
AND HudCache.gametypeId in FROM Hands h
(SELECT gt1.id from Gametypes gt1, Gametypes gt2, Hands INNER JOIN HandsPlayers hp ON (hp.handId = %s)
WHERE gt1.siteid = gt2.siteid INNER JOIN HudCache hc ON (hc.playerId = hp.playerId)
AND gt1.type = gt2.type INNER JOIN Players p ON (p.id = hc.playerId)
AND gt1.category = gt2.category WHERE h.id = %s
AND gt1.limittype = gt2.limittype AND hc.styleKey > %s
AND gt2.id = Hands.gametypeId /* styleKey is currently 'd' (for date) followed by a yyyymmdd
AND Hands.id = %s) date key. Set it to 0000000 or similar to get all records */
GROUP BY HudCache.PlayerId /* also check activeseats here? even if only 3 groups eg 2-3/4-6/7+ ??
e.g. could use a multiplier:
AND h.seats > %s / 1.25 and hp.seats < %s * 1.25
where %s is the number of active players at the current table (and
1.25 would be a config value so user could change it)
*/
AND hc.gametypeId+0 in
(SELECT gt1.id from Gametypes gt1, Gametypes gt2
WHERE gt1.siteid = gt2.siteid
AND gt1.type = gt2.type
AND gt1.category = gt2.category
AND gt1.limittype = gt2.limittype
AND gt2.id = h.gametypeId)
GROUP BY hc.PlayerId, p.name, hc.styleKey
""" """
if db_server == 'mysql':
self.query['get_stats_from_hand_session'] = """
SELECT hp.playerId AS player_id,
hp.handId AS hand_id,
hp.seatNo AS seat,
p.name AS screen_name,
h.seats AS seats,
1 AS n,
cast(hp2.street0VPI as <signed>integer) AS vpip,
cast(hp2.street0Aggr as <signed>integer) AS pfr,
cast(hp2.street0_3BChance as <signed>integer) AS TB_opp_0,
cast(hp2.street0_3BDone as <signed>integer) AS TB_0,
cast(hp2.street1Seen as <signed>integer) AS saw_f,
cast(hp2.street1Seen as <signed>integer) AS saw_1,
cast(hp2.street2Seen as <signed>integer) AS saw_2,
cast(hp2.street3Seen as <signed>integer) AS saw_3,
cast(hp2.street4Seen as <signed>integer) AS saw_4,
cast(hp2.sawShowdown as <signed>integer) AS sd,
cast(hp2.street1Aggr as <signed>integer) AS aggr_1,
cast(hp2.street2Aggr as <signed>integer) AS aggr_2,
cast(hp2.street3Aggr as <signed>integer) AS aggr_3,
cast(hp2.street4Aggr as <signed>integer) AS aggr_4,
cast(hp2.otherRaisedStreet1 as <signed>integer) AS was_raised_1,
cast(hp2.otherRaisedStreet2 as <signed>integer) AS was_raised_2,
cast(hp2.otherRaisedStreet3 as <signed>integer) AS was_raised_3,
cast(hp2.otherRaisedStreet4 as <signed>integer) AS was_raised_4,
cast(hp2.foldToOtherRaisedStreet1 as <signed>integer) AS f_freq_1,
cast(hp2.foldToOtherRaisedStreet2 as <signed>integer) AS f_freq_2,
cast(hp2.foldToOtherRaisedStreet3 as <signed>integer) AS f_freq_3,
cast(hp2.foldToOtherRaisedStreet4 as <signed>integer) AS f_freq_4,
cast(hp2.wonWhenSeenStreet1 as <signed>integer) AS w_w_s_1,
cast(hp2.wonAtSD as <signed>integer) AS wmsd,
cast(hp2.stealAttemptChance as <signed>integer) AS steal_opp,
cast(hp2.stealAttempted as <signed>integer) AS steal,
cast(hp2.foldSbToStealChance as <signed>integer) AS SBstolen,
cast(hp2.foldedSbToSteal as <signed>integer) AS SBnotDef,
cast(hp2.foldBbToStealChance as <signed>integer) AS BBstolen,
cast(hp2.foldedBbToSteal as <signed>integer) AS BBnotDef,
cast(hp2.street1CBChance as <signed>integer) AS CB_opp_1,
cast(hp2.street1CBDone as <signed>integer) AS CB_1,
cast(hp2.street2CBChance as <signed>integer) AS CB_opp_2,
cast(hp2.street2CBDone as <signed>integer) AS CB_2,
cast(hp2.street3CBChance as <signed>integer) AS CB_opp_3,
cast(hp2.street3CBDone as <signed>integer) AS CB_3,
cast(hp2.street4CBChance as <signed>integer) AS CB_opp_4,
cast(hp2.street4CBDone as <signed>integer) AS CB_4,
cast(hp2.foldToStreet1CBChance as <signed>integer) AS f_cb_opp_1,
cast(hp2.foldToStreet1CBDone as <signed>integer) AS f_cb_1,
cast(hp2.foldToStreet2CBChance as <signed>integer) AS f_cb_opp_2,
cast(hp2.foldToStreet2CBDone as <signed>integer) AS f_cb_2,
cast(hp2.foldToStreet3CBChance as <signed>integer) AS f_cb_opp_3,
cast(hp2.foldToStreet3CBDone as <signed>integer) AS f_cb_3,
cast(hp2.foldToStreet4CBChance as <signed>integer) AS f_cb_opp_4,
cast(hp2.foldToStreet4CBDone as <signed>integer) AS f_cb_4,
cast(hp2.totalProfit as <signed>integer) AS net,
cast(hp2.street1CheckCallRaiseChance as <signed>integer) AS ccr_opp_1,
cast(hp2.street1CheckCallRaiseDone as <signed>integer) AS ccr_1,
cast(hp2.street2CheckCallRaiseChance as <signed>integer) AS ccr_opp_2,
cast(hp2.street2CheckCallRaiseDone as <signed>integer) AS ccr_2,
cast(hp2.street3CheckCallRaiseChance as <signed>integer) AS ccr_opp_3,
cast(hp2.street3CheckCallRaiseDone as <signed>integer) AS ccr_3,
cast(hp2.street4CheckCallRaiseChance as <signed>integer) AS ccr_opp_4,
cast(hp2.street4CheckCallRaiseDone as <signed>integer) AS ccr_4
FROM
Hands h /* players in this hand */
INNER JOIN Hands h2 ON (h2.id > %s AND h2.tableName = h.tableName)
INNER JOIN HandsPlayers hp ON (h.id = hp.handId)
INNER JOIN HandsPlayers hp2 ON (hp2.playerId+0 = hp.playerId+0 AND (hp2.handId = h2.id+0)) /* other hands by these players */
INNER JOIN Players p ON (p.id = hp2.PlayerId+0)
WHERE hp.handId = %s
/* check activeseats once this data returned? (don't want to do that here as it might
assume a session ended just because the number of seats dipped for a few hands)
*/
ORDER BY h.handStart desc, hp2.PlayerId
/* order rows by handstart descending so that we can stop reading rows when
there's a gap over X minutes between hands (ie. when we get back to start of
the session */
"""
else: # assume postgresql
self.query['get_stats_from_hand_session'] = """
SELECT hp.playerId AS player_id,
hp.handId AS hand_id,
hp.seatNo AS seat,
p.name AS screen_name,
h.seats AS seats,
1 AS n,
cast(hp2.street0VPI as <signed>integer) AS vpip,
cast(hp2.street0Aggr as <signed>integer) AS pfr,
cast(hp2.street0_3BChance as <signed>integer) AS TB_opp_0,
cast(hp2.street0_3BDone as <signed>integer) AS TB_0,
cast(hp2.street1Seen as <signed>integer) AS saw_f,
cast(hp2.street1Seen as <signed>integer) AS saw_1,
cast(hp2.street2Seen as <signed>integer) AS saw_2,
cast(hp2.street3Seen as <signed>integer) AS saw_3,
cast(hp2.street4Seen as <signed>integer) AS saw_4,
cast(hp2.sawShowdown as <signed>integer) AS sd,
cast(hp2.street1Aggr as <signed>integer) AS aggr_1,
cast(hp2.street2Aggr as <signed>integer) AS aggr_2,
cast(hp2.street3Aggr as <signed>integer) AS aggr_3,
cast(hp2.street4Aggr as <signed>integer) AS aggr_4,
cast(hp2.otherRaisedStreet1 as <signed>integer) AS was_raised_1,
cast(hp2.otherRaisedStreet2 as <signed>integer) AS was_raised_2,
cast(hp2.otherRaisedStreet3 as <signed>integer) AS was_raised_3,
cast(hp2.otherRaisedStreet4 as <signed>integer) AS was_raised_4,
cast(hp2.foldToOtherRaisedStreet1 as <signed>integer) AS f_freq_1,
cast(hp2.foldToOtherRaisedStreet2 as <signed>integer) AS f_freq_2,
cast(hp2.foldToOtherRaisedStreet3 as <signed>integer) AS f_freq_3,
cast(hp2.foldToOtherRaisedStreet4 as <signed>integer) AS f_freq_4,
cast(hp2.wonWhenSeenStreet1 as <signed>integer) AS w_w_s_1,
cast(hp2.wonAtSD as <signed>integer) AS wmsd,
cast(hp2.stealAttemptChance as <signed>integer) AS steal_opp,
cast(hp2.stealAttempted as <signed>integer) AS steal,
cast(hp2.foldSbToStealChance as <signed>integer) AS SBstolen,
cast(hp2.foldedSbToSteal as <signed>integer) AS SBnotDef,
cast(hp2.foldBbToStealChance as <signed>integer) AS BBstolen,
cast(hp2.foldedBbToSteal as <signed>integer) AS BBnotDef,
cast(hp2.street1CBChance as <signed>integer) AS CB_opp_1,
cast(hp2.street1CBDone as <signed>integer) AS CB_1,
cast(hp2.street2CBChance as <signed>integer) AS CB_opp_2,
cast(hp2.street2CBDone as <signed>integer) AS CB_2,
cast(hp2.street3CBChance as <signed>integer) AS CB_opp_3,
cast(hp2.street3CBDone as <signed>integer) AS CB_3,
cast(hp2.street4CBChance as <signed>integer) AS CB_opp_4,
cast(hp2.street4CBDone as <signed>integer) AS CB_4,
cast(hp2.foldToStreet1CBChance as <signed>integer) AS f_cb_opp_1,
cast(hp2.foldToStreet1CBDone as <signed>integer) AS f_cb_1,
cast(hp2.foldToStreet2CBChance as <signed>integer) AS f_cb_opp_2,
cast(hp2.foldToStreet2CBDone as <signed>integer) AS f_cb_2,
cast(hp2.foldToStreet3CBChance as <signed>integer) AS f_cb_opp_3,
cast(hp2.foldToStreet3CBDone as <signed>integer) AS f_cb_3,
cast(hp2.foldToStreet4CBChance as <signed>integer) AS f_cb_opp_4,
cast(hp2.foldToStreet4CBDone as <signed>integer) AS f_cb_4,
cast(hp2.totalProfit as <signed>integer) AS net,
cast(hp2.street1CheckCallRaiseChance as <signed>integer) AS ccr_opp_1,
cast(hp2.street1CheckCallRaiseDone as <signed>integer) AS ccr_1,
cast(hp2.street2CheckCallRaiseChance as <signed>integer) AS ccr_opp_2,
cast(hp2.street2CheckCallRaiseDone as <signed>integer) AS ccr_2,
cast(hp2.street3CheckCallRaiseChance as <signed>integer) AS ccr_opp_3,
cast(hp2.street3CheckCallRaiseDone as <signed>integer) AS ccr_3,
cast(hp2.street4CheckCallRaiseChance as <signed>integer) AS ccr_opp_4,
cast(hp2.street4CheckCallRaiseDone as <signed>integer) AS ccr_4
FROM Hands h /* this hand */
INNER JOIN Hands h2 ON ( h2.id > %s /* other hands */
AND h2.tableName = h.tableName)
INNER JOIN HandsPlayers hp ON (h.id = hp.handId) /* players in this hand */
INNER JOIN HandsPlayers hp2 ON ( hp2.playerId+0 = hp.playerId+0
AND hp2.handId = h2.id) /* other hands by these players */
INNER JOIN Players p ON (p.id = hp2.PlayerId+0)
WHERE h.id = %s
/* check activeseats once this data returned? (don't want to do that here as it might
assume a session ended just because the number of seats dipped for a few hands)
*/
ORDER BY h.handStart desc, hp2.PlayerId
/* order rows by handstart descending so that we can stop reading rows when
there's a gap over X minutes between hands (ie. when we get back to start of
the session */
"""
self.query['get_players_from_hand'] = """ self.query['get_players_from_hand'] = """
SELECT HandsPlayers.playerId, seatNo, name SELECT HandsPlayers.playerId, seatNo, name
FROM HandsPlayers INNER JOIN Players ON (HandsPlayers.playerId = Players.id) FROM HandsPlayers INNER JOIN Players ON (HandsPlayers.playerId = Players.id)
@ -331,7 +511,7 @@ class Sql:
""" """
self.query['get_table_name'] = """ self.query['get_table_name'] = """
select tableName, maxSeats, category select tableName, maxSeats, category, type
from Hands,Gametypes from Hands,Gametypes
where Hands.id = %s where Hands.id = %s
and Gametypes.id = Hands.gametypeId and Gametypes.id = Hands.gametypeId
@ -349,13 +529,13 @@ class Sql:
select select
seatNo AS seat_number, seatNo AS seat_number,
name AS screen_name, name AS screen_name,
card1Value, card1Suit, card1, /*card1Value, card1Suit, */
card2Value, card2Suit, card2, /*card2Value, card2Suit, */
card3Value, card3Suit, card3, /*card3Value, card3Suit, */
card4Value, card4Suit, card4, /*card4Value, card4Suit, */
card5Value, card5Suit, card5, /*card5Value, card5Suit, */
card6Value, card6Suit, card6, /*card6Value, card6Suit, */
card7Value, card7Suit card7 /*card7Value, card7Suit */
from HandsPlayers, Players from HandsPlayers, Players
where handID = %s and HandsPlayers.playerId = Players.id where handID = %s and HandsPlayers.playerId = Players.id
order by seatNo order by seatNo
@ -377,9 +557,21 @@ class Sql:
FROM Players, HandsActions, HandsPlayers FROM Players, HandsActions, HandsPlayers
WHERE HandsPlayers.handid = %s WHERE HandsPlayers.handid = %s
AND HandsPlayers.playerid = Players.id AND HandsPlayers.playerid = Players.id
AND HandsActions.handPlayerId = HandsPlayers.id AND HandsActions.handsPlayerId = HandsPlayers.id
ORDER BY street, actionno ORDER BY street, actionno
""" """
if db_server == 'mysql':
self.query['get_hand_1day_ago'] = """
select coalesce(max(id),0)
from hands
where handstart < date_sub(utc_timestamp(), interval '1' day)"""
else: # assume postgresql
self.query['get_hand_1day_ago'] = """
select coalesce(max(id),0)
from hands
where handstart < now() at time zone 'UTC' - interval '1 day'"""
if __name__== "__main__": if __name__== "__main__":
# just print the default queries and exit # just print the default queries and exit
s = Sql(game = 'razz', type = 'ptracks') s = Sql(game = 'razz', type = 'ptracks')

View File

@ -32,6 +32,8 @@
# float(stat_dict[player]['vpip'])/float(stat_dict[player]['n']). You can see how the # float(stat_dict[player]['vpip'])/float(stat_dict[player]['n']). You can see how the
# keys of stat_dict relate to the column names in HudCache by inspecting # keys of stat_dict relate to the column names in HudCache by inspecting
# the proper section of the SQL.py module. # the proper section of the SQL.py module.
# The stat_dict keys should be in lower case, i.e. vpip not VPIP, since
# postgres returns the column names in lower case.
# 3 You have to write a small function for each stat you want to add. See # 3 You have to write a small function for each stat you want to add. See
# the vpip() function for example. This function has to be protected from # the vpip() function for example. This function has to be protected from
# exceptions, using something like the try:/except: paragraphs in vpip. # exceptions, using something like the try:/except: paragraphs in vpip.
@ -189,6 +191,27 @@ def wtsd(stat_dict, player):
'% went to showdown' '% went to showdown'
) )
def wtsd_0(stat_dict, player):
""" Went to SD when saw flop/4th."""
stat = 0.0
try:
stat = float(stat_dict[player]['sd'])/float(stat_dict[player]['saw_f'])
return (stat,
'%2.0f' % (100*stat) + '%',
'w=%2.0f' % (100*stat) + '%',
'wtsd=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['sd'], stat_dict[player]['saw_f']),
'% went to showdown'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'w=%2.0f' % (0) + '%',
'wtsd=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% went to showdown'
)
def wmsd(stat_dict, player): def wmsd(stat_dict, player):
""" Won $ at showdown.""" """ Won $ at showdown."""
stat = 0.0 stat = 0.0
@ -210,6 +233,27 @@ def wmsd(stat_dict, player):
'% won money at showdown' '% won money at showdown'
) )
def wmsd_0(stat_dict, player):
""" Won $ at showdown."""
stat = 0.0
try:
stat = float(stat_dict[player]['wmsd'])/float(stat_dict[player]['sd'])
return (stat,
'%2.0f' % (100*stat) + '%',
'w=%2.0f' % (100*stat) + '%',
'wmsd=%2.0f' % (100*stat) + '%',
'(%5.1f/%d)' % (float(stat_dict[player]['wmsd']), stat_dict[player]['sd']),
'% won money at showdown'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'w=%2.0f' % (0) + '%',
'wmsd=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% won money at showdown'
)
def profit100_0(stat_dict, player): def profit100_0(stat_dict, player):
""" Profit won per 100 hands (no decimal places).""" """ Profit won per 100 hands (no decimal places)."""
stat = 0.0 stat = 0.0
@ -312,16 +356,37 @@ def steal(stat_dict, player):
except: except:
return (stat, 'NA', 'st=NA', 'steal=NA', '(0/0)', '% steal attempted') return (stat, 'NA', 'st=NA', 'steal=NA', '(0/0)', '% steal attempted')
def steal_0(stat_dict, player):
""" Steal %."""
stat = 0.0
try:
stat = float(stat_dict[player]['steal'])/float(stat_dict[player]['steal_opp'])
return (stat,
'%2.0f' % (100*stat) + '%',
'st=%2.0f' % (100*stat) + '%',
'steal=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['steal'], stat_dict[player]['steal_opp']),
'% steal attempted'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'st=%2.0f' % (0) + '%',
'steal=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% steal attempted'
)
def f_SB_steal(stat_dict, player): def f_SB_steal(stat_dict, player):
""" Folded SB to steal.""" """ Folded SB to steal."""
stat = 0.0 stat = 0.0
try: try:
stat = float(stat_dict[player]['SBnotDef'])/float(stat_dict[player]['SBstolen']) stat = float(stat_dict[player]['sbnotdef'])/float(stat_dict[player]['sbstolen'])
return (stat, return (stat,
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'fSB=%3.1f' % (100*stat) + '%', 'fSB=%3.1f' % (100*stat) + '%',
'fSB_s=%3.1f' % (100*stat) + '%', 'fSB_s=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['SBnotDef'], stat_dict[player]['SBstolen']), '(%d/%d)' % (stat_dict[player]['sbnotdef'], stat_dict[player]['sbstolen']),
'% folded SB to steal' '% folded SB to steal'
) )
except: except:
@ -336,12 +401,32 @@ def f_BB_steal(stat_dict, player):
""" Folded BB to steal.""" """ Folded BB to steal."""
stat = 0.0 stat = 0.0
try: try:
stat = float(stat_dict[player]['BBnotDef'])/float(stat_dict[player]['BBstolen']) stat = float(stat_dict[player]['bbnotdef'])/float(stat_dict[player]['bbstolen'])
return (stat, return (stat,
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'fBB=%3.1f' % (100*stat) + '%', 'fBB=%3.1f' % (100*stat) + '%',
'fBB_s=%3.1f' % (100*stat) + '%', 'fBB_s=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['BBnotDef'], stat_dict[player]['BBstolen']), '(%d/%d)' % (stat_dict[player]['bbnotdef'], stat_dict[player]['bbstolen']),
'% folded BB to steal'
)
except:
return (stat,
'NA',
'fBB=NA',
'fBB_s=NA',
'(0/0)',
'% folded BB to steal')
def f_BB_steal_0(stat_dict, player):
""" Folded BB to steal."""
stat = 0.0
try:
stat = float(stat_dict[player]['bbnotdef'])/float(stat_dict[player]['bbstolen'])
return (stat,
'%2.0f' % (100*stat) + '%',
'fBB=%2.0f' % (100*stat) + '%',
'fBB_s=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['bbnotdef'], stat_dict[player]['bbstolen']),
'% folded BB to steal' '% folded BB to steal'
) )
except: except:
@ -356,12 +441,12 @@ def three_B_0(stat_dict, player):
""" Three bet preflop/3rd.""" """ Three bet preflop/3rd."""
stat = 0.0 stat = 0.0
try: try:
stat = float(stat_dict[player]['TB_0'])/float(stat_dict[player]['TB_opp_0']) stat = float(stat_dict[player]['tb_0'])/float(stat_dict[player]['tb_opp_0'])
return (stat, return (stat,
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'3B=%3.1f' % (100*stat) + '%', '3B=%3.1f' % (100*stat) + '%',
'3B_pf=%3.1f' % (100*stat) + '%', '3B_pf=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['TB_0'], stat_dict[player]['TB_opp_0']), '(%d/%d)' % (stat_dict[player]['tb_0'], stat_dict[player]['tb_opp_0']),
'% 3/4 Bet preflop/3rd' '% 3/4 Bet preflop/3rd'
) )
except: except:
@ -537,12 +622,12 @@ def cb_1(stat_dict, player):
""" Flop continuation bet.""" """ Flop continuation bet."""
stat = 0.0 stat = 0.0
try: try:
stat = float(stat_dict[player]['CB_1'])/float(stat_dict[player]['CB_opp_1']) stat = float(stat_dict[player]['cb_1'])/float(stat_dict[player]['cb_opp_1'])
return (stat, return (stat,
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'cb1=%3.1f' % (100*stat) + '%', 'cb1=%3.1f' % (100*stat) + '%',
'cb_1=%3.1f' % (100*stat) + '%', 'cb_1=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['CB_1'], stat_dict[player]['CB_opp_1']), '(%d/%d)' % (stat_dict[player]['cb_1'], stat_dict[player]['cb_opp_1']),
'% continuation bet flop/4th' '% continuation bet flop/4th'
) )
except: except:
@ -558,12 +643,12 @@ def cb_2(stat_dict, player):
""" Turn continuation bet.""" """ Turn continuation bet."""
stat = 0.0 stat = 0.0
try: try:
stat = float(stat_dict[player]['CB_2'])/float(stat_dict[player]['CB_opp_2']) stat = float(stat_dict[player]['cb_2'])/float(stat_dict[player]['cb_opp_2'])
return (stat, return (stat,
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'cb2=%3.1f' % (100*stat) + '%', 'cb2=%3.1f' % (100*stat) + '%',
'cb_2=%3.1f' % (100*stat) + '%', 'cb_2=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['CB_2'], stat_dict[player]['CB_opp_2']), '(%d/%d)' % (stat_dict[player]['cb_2'], stat_dict[player]['cb_opp_2']),
'% continuation bet turn/5th' '% continuation bet turn/5th'
) )
except: except:
@ -579,12 +664,12 @@ def cb_3(stat_dict, player):
""" River continuation bet.""" """ River continuation bet."""
stat = 0.0 stat = 0.0
try: try:
stat = float(stat_dict[player]['CB_3'])/float(stat_dict[player]['CB_opp_3']) stat = float(stat_dict[player]['cb_3'])/float(stat_dict[player]['cb_opp_3'])
return (stat, return (stat,
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'cb3=%3.1f' % (100*stat) + '%', 'cb3=%3.1f' % (100*stat) + '%',
'cb_3=%3.1f' % (100*stat) + '%', 'cb_3=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['CB_3'], stat_dict[player]['CB_opp_3']), '(%d/%d)' % (stat_dict[player]['cb_3'], stat_dict[player]['cb_opp_3']),
'% continuation bet river/6th' '% continuation bet river/6th'
) )
except: except:
@ -600,12 +685,12 @@ def cb_4(stat_dict, player):
""" 7th street continuation bet.""" """ 7th street continuation bet."""
stat = 0.0 stat = 0.0
try: try:
stat = float(stat_dict[player]['CB_4'])/float(stat_dict[player]['CB_opp_4']) stat = float(stat_dict[player]['cb_4'])/float(stat_dict[player]['cb_opp_4'])
return (stat, return (stat,
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'cb4=%3.1f' % (100*stat) + '%', 'cb4=%3.1f' % (100*stat) + '%',
'cb_4=%3.1f' % (100*stat) + '%', 'cb_4=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['CB_4'], stat_dict[player]['CB_opp_4']), '(%d/%d)' % (stat_dict[player]['cb_4'], stat_dict[player]['cb_opp_4']),
'% continuation bet 7th' '% continuation bet 7th'
) )
except: except:
@ -719,10 +804,10 @@ if __name__== "__main__":
print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f') print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsd') print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsd')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_SB_steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_sb_steal')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_BB_steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_bb_steal')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_B_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_b_0')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'WMsF') print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsf')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_1')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3')

View File

@ -135,7 +135,7 @@ def discover_posix(c):
if re.search(params['table_finder'], listing): if re.search(params['table_finder'], listing):
if 'Lobby' in listing: continue if 'Lobby' in listing: continue
if 'Instant Hand History' in listing: continue if 'Instant Hand History' in listing: continue
if '\"Full Tilt Poker\"' in listing: continue # if '\"Full Tilt Poker\"' in listing: continue
if 'History for table:' in listing: continue if 'History for table:' in listing: continue
if 'has no name' in listing: continue if 'has no name' in listing: continue
info = decode_xwininfo(c, listing) info = decode_xwininfo(c, listing)
@ -391,7 +391,7 @@ def discover_mac_by_name(c, tablename):
if __name__=="__main__": if __name__=="__main__":
c = Configuration.Config() c = Configuration.Config()
print discover_table_by_name(c, "Ringe") print discover_table_by_name(c, "Torino")
# print discover_tournament_table(c, "118942908", "3") # print discover_tournament_table(c, "118942908", "3")
tables = discover(c) tables = discover(c)

View File

@ -44,10 +44,11 @@ import GuiPositionalStats
import GuiTableViewer import GuiTableViewer
import GuiAutoImport import GuiAutoImport
import GuiGraphViewer import GuiGraphViewer
import GuiSessionViewer
import FpdbSQLQueries import FpdbSQLQueries
import Configuration import Configuration
VERSION = "0.10" VERSION = "0.11"
class fpdb: class fpdb:
def tab_clicked(self, widget, tab_name): def tab_clicked(self, widget, tab_name):
@ -105,30 +106,36 @@ class fpdb:
#end def delete_event #end def delete_event
def destroy(self, widget, data=None): def destroy(self, widget, data=None):
self.quit(widget, data) self.quit(widget)
#end def destroy #end def destroy
def dia_about(self, widget, data): def dia_about(self, widget, data=None):
print "todo: implement dia_about", print "todo: implement dia_about",
print " version = %s, requires database version %s" % (VERSION, "118") print " version = %s, requires database version %s" % (VERSION, "118")
#end def dia_about #end def dia_about
def dia_create_del_database(self, widget, data): def dia_create_del_database(self, widget, data=None):
print "todo: implement dia_create_del_database" print "todo: implement dia_create_del_database"
self.obtain_global_lock() self.obtain_global_lock()
#end def dia_create_del_database #end def dia_create_del_database
def dia_create_del_user(self, widget, data): def dia_create_del_user(self, widget, data=None):
print "todo: implement dia_create_del_user" print "todo: implement dia_create_del_user"
self.obtain_global_lock() self.obtain_global_lock()
#end def dia_create_del_user #end def dia_create_del_user
def dia_database_stats(self, widget, data): def dia_database_stats(self, widget, data=None):
print "todo: implement dia_database_stats" print "todo: implement dia_database_stats"
#string=fpdb_db.getDbStats(db, cursor) #string=fpdb_db.getDbStats(db, cursor)
#end def dia_database_stats #end def dia_database_stats
def dia_delete_db_parts(self, widget, data): def dia_database_sessions(self, widget, data=None):
new_sessions_thread=GuiSessionViewer.GuiSessionViewer(self.config, self.querydict)
self.threads.append(new_sessions_thread)
sessions_tab=new_sessions_thread.get_vbox()
self.add_and_display_tab(sessions_tab, "Sessions")
def dia_delete_db_parts(self, widget, data=None):
print "todo: implement dia_delete_db_parts" print "todo: implement dia_delete_db_parts"
self.obtain_global_lock() self.obtain_global_lock()
#end def dia_delete_db_parts #end def dia_delete_db_parts
@ -138,7 +145,7 @@ class fpdb:
self.obtain_global_lock() self.obtain_global_lock()
#end def dia_edit_profile #end def dia_edit_profile
def dia_export_db(self, widget, data): def dia_export_db(self, widget, data=None):
print "todo: implement dia_export_db" print "todo: implement dia_export_db"
self.obtain_global_lock() self.obtain_global_lock()
#end def dia_export_db #end def dia_export_db
@ -163,16 +170,16 @@ class fpdb:
# return (user, pw, response) # return (user, pw, response)
#end def dia_get_db_root_credentials #end def dia_get_db_root_credentials
def dia_import_db(self, widget, data): def dia_import_db(self, widget, data=None):
print "todo: implement dia_import_db" print "todo: implement dia_import_db"
self.obtain_global_lock() self.obtain_global_lock()
#end def dia_import_db #end def dia_import_db
def dia_licensing(self, widget, data): def dia_licensing(self, widget, data=None):
print "todo: implement dia_licensing" print "todo: implement dia_licensing"
#end def dia_licensing #end def dia_licensing
def dia_load_profile(self, widget, data): def dia_load_profile(self, widget, data=None):
"""Dialogue to select a file to load a profile from""" """Dialogue to select a file to load a profile from"""
self.obtain_global_lock() self.obtain_global_lock()
chooser = gtk.FileChooserDialog(title="Please select a profile file to load", chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
@ -188,7 +195,7 @@ class fpdb:
print 'User cancelled loading profile' print 'User cancelled loading profile'
#end def dia_load_profile #end def dia_load_profile
def dia_recreate_tables(self, widget, data): def dia_recreate_tables(self, widget, data=None):
"""Dialogue that asks user to confirm that he wants to delete and recreate the tables""" """Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
self.obtain_global_lock() self.obtain_global_lock()
@ -205,12 +212,12 @@ class fpdb:
print 'User cancelled recreating tables' print 'User cancelled recreating tables'
#end def dia_recreate_tables #end def dia_recreate_tables
def dia_regression_test(self, widget, data): def dia_regression_test(self, widget, data=None):
print "todo: implement dia_regression_test" print "todo: implement dia_regression_test"
self.obtain_global_lock() self.obtain_global_lock()
#end def dia_regression_test #end def dia_regression_test
def dia_save_profile(self, widget, data): def dia_save_profile(self, widget, data=None):
print "todo: implement dia_save_profile" print "todo: implement dia_save_profile"
#end def dia_save_profile #end def dia_save_profile
@ -237,11 +244,89 @@ class fpdb:
def get_menu(self, window): def get_menu(self, window):
"""returns the menu for this program""" """returns the menu for this program"""
accel_group = gtk.AccelGroup() fpdbmenu = """
self.item_factory = gtk.ItemFactory(gtk.MenuBar, "<main>", accel_group) <ui>
self.item_factory.create_items(self.menu_items) <menubar name="MenuBar">
<menu action="main">
<menuitem action="LoadProf"/>
<menuitem action="EditProf"/>
<menuitem action="SaveProf"/>
<separator/>
<menuitem action="Quit"/>
</menu>
<menu action="import">
<menuitem action="bulkimp"/>
<menuitem action="autoimp"/>
<menuitem action="autorate"/>
</menu>
<menu action="viewers">
<menuitem action="autoimp"/>
<menuitem action="graphs"/>
<menuitem action="handreplay"/>
<menuitem action="playerdetails"/>
<menuitem action="playerstats"/>
<menuitem action="posnstats"/>
<menuitem action="sessionreplay"/>
<menuitem action="tableviewer"/>
</menu>
<menu action="database">
<menuitem action="createdb"/>
<menuitem action="createuser"/>
<menuitem action="createtabs"/>
<menuitem action="stats"/>
<menuitem action="sessions"/>
</menu>
<menu action="help">
<menuitem action="Abbrev"/>
<separator/>
<menuitem action="About"/>
<menuitem action="License"/>
</menu>
</menubar>
</ui>"""
uimanager = gtk.UIManager()
accel_group = uimanager.get_accel_group()
actiongroup = gtk.ActionGroup('UIManagerExample')
# Create actions
actiongroup.add_actions([('main', None, '_Main'),
('Quit', gtk.STOCK_QUIT, '_Quit me!', None, 'Quit the Program', self.quit),
('LoadProf', None, '_Load Profile (broken)', '<control>L', 'Load your profile', self.dia_load_profile),
('EditProf', None, '_Edit Profile (todo)', '<control>E', 'Edit your profile', self.dia_edit_profile),
('SaveProf', None, '_Save Profile (todo)', '<control>S', 'Save your profile', self.dia_save_profile),
('import', None, '_Import'),
('bulkimp', None, '_Bulk Import', '<control>B', 'Bulk Import', self.tab_bulk_import),
('autorate', None, 'Auto _Rating (todo)', '<control>R', 'Auto Rating (todo)', self.not_implemented),
('viewers', None, '_Viewers'),
('autoimp', None, '_Auto Import and HUD', '<control>A', 'Auto Import and HUD', self.tab_auto_import),
('graphs', None, '_Graphs', '<control>G', 'Graphs', self.tabGraphViewer),
('handreplay', None, 'Hand _Replayer (todo)', None, 'Hand Replayer (todo)', self.not_implemented),
('playerdetails', None, 'Player _Details (todo)', None, 'Player Details (todo)', self.not_implemented),
('playerstats', None, '_Player Stats (tabulated view)', '<control>P', 'Player Stats (tabulated view)', self.tab_player_stats),
('posnstats', None, 'P_ositional Stats (tabulated view)', '<control>O', 'Positional Stats (tabulated view)', self.tab_positional_stats),
('sessionreplay', None, '_Session Replayer (todo)', None, 'Session Replayer (todo)', self.not_implemented),
('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer),
('database', None, '_Database'),
('createdb', None, 'Create or Delete _Database (todo)', None, 'Create or Delete Database', self.dia_create_del_database),
('createuser', None, 'Create or Delete _User (todo)', None, 'Create or Delete User', self.dia_create_del_user),
('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables),
('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats),
('sessions', None, 'Sessions', None, 'View Sessions', self.dia_database_sessions),
('help', None, '_Help'),
('Abbrev', None, '_Abbrevations (todo)', None, 'List of Abbrevations', self.tab_abbreviations),
('About', None, 'A_bout', None, 'About the program', self.dia_about),
('License', None, '_License and Copying (todo)', None, 'License and Copying', self.dia_licensing),
])
actiongroup.get_action('Quit').set_property('short-label', '_Quit')
uimanager.insert_action_group(actiongroup, 0)
merge_id = uimanager.add_ui_from_string(fpdbmenu)
# Create a MenuBar
menubar = uimanager.get_widget('/MenuBar')
window.add_accel_group(accel_group) window.add_accel_group(accel_group)
return self.item_factory.get_widget("<main>") return menubar
#end def get_menu #end def get_menu
def load_profile(self): def load_profile(self):
@ -287,9 +372,10 @@ class fpdb:
# Database connected to successfully, load queries to pass on to other classes # Database connected to successfully, load queries to pass on to other classes
self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name()) self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
self.db.db.rollback()
#end def load_profile #end def load_profile
def not_implemented(self): def not_implemented(self, widget, data=None):
print "todo: called unimplemented menu entry (users: pls ignore this)"#remove this once more entries are implemented print "todo: called unimplemented menu entry (users: pls ignore this)"#remove this once more entries are implemented
#end def not_implemented #end def not_implemented
@ -297,7 +383,7 @@ class fpdb:
print "todo: implement obtain_global_lock (users: pls ignore this)" print "todo: implement obtain_global_lock (users: pls ignore this)"
#end def obtain_global_lock #end def obtain_global_lock
def quit(self, widget, data): def quit(self, widget):
print "Quitting normally" print "Quitting normally"
#check if current settings differ from profile, if so offer to save or abort #check if current settings differ from profile, if so offer to save or abort
self.db.disconnect() self.db.disconnect()
@ -308,11 +394,11 @@ class fpdb:
print "todo: implement release_global_lock" print "todo: implement release_global_lock"
#end def release_global_lock #end def release_global_lock
def tab_abbreviations(self, widget, data): def tab_abbreviations(self, widget, data=None):
print "todo: implement tab_abbreviations" print "todo: implement tab_abbreviations"
#end def tab_abbreviations #end def tab_abbreviations
def tab_auto_import(self, widget, data): def tab_auto_import(self, widget, data=None):
"""opens the auto import tab""" """opens the auto import tab"""
new_aimp_thread=GuiAutoImport.GuiAutoImport(self.settings, self.config) new_aimp_thread=GuiAutoImport.GuiAutoImport(self.settings, self.config)
self.threads.append(new_aimp_thread) self.threads.append(new_aimp_thread)
@ -320,29 +406,29 @@ class fpdb:
self.add_and_display_tab(aimp_tab, "Auto Import") self.add_and_display_tab(aimp_tab, "Auto Import")
#end def tab_auto_import #end def tab_auto_import
def tab_bulk_import(self, widget, data): def tab_bulk_import(self, widget, data=None):
"""opens a tab for bulk importing""" """opens a tab for bulk importing"""
#print "start of tab_bulk_import" #print "start of tab_bulk_import"
new_import_thread=GuiBulkImport.GuiBulkImport(self.db, self.settings, self.config) new_import_thread=GuiBulkImport.GuiBulkImport(self.settings, self.config)
self.threads.append(new_import_thread) self.threads.append(new_import_thread)
bulk_tab=new_import_thread.get_vbox() bulk_tab=new_import_thread.get_vbox()
self.add_and_display_tab(bulk_tab, "Bulk Import") self.add_and_display_tab(bulk_tab, "Bulk Import")
#end def tab_bulk_import #end def tab_bulk_import
def tab_player_stats(self, widget, data): def tab_player_stats(self, widget, data=None):
new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.db, self.config, self.querydict) new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.config, self.querydict, self.window)
self.threads.append(new_ps_thread) self.threads.append(new_ps_thread)
ps_tab=new_ps_thread.get_vbox() ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Player Stats") self.add_and_display_tab(ps_tab, "Player Stats")
def tab_positional_stats(self, widget, data): def tab_positional_stats(self, widget, data=None):
new_ps_thread=GuiPositionalStats.GuiPositionalStats(self.db, self.config, self.querydict) new_ps_thread=GuiPositionalStats.GuiPositionalStats(self.config, self.querydict)
self.threads.append(new_ps_thread) self.threads.append(new_ps_thread)
ps_tab=new_ps_thread.get_vbox() ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Positional Stats") self.add_and_display_tab(ps_tab, "Positional Stats")
def tab_main_help(self, widget, data): def tab_main_help(self, widget, data=None):
"""Displays a tab with the main fpdb help screen""" """Displays a tab with the main fpdb help screen"""
#print "start of tab_main_help" #print "start of tab_main_help"
mh_tab=gtk.Label("""Welcome to Fpdb! mh_tab=gtk.Label("""Welcome to Fpdb!
@ -352,7 +438,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.add_and_display_tab(mh_tab, "Help") self.add_and_display_tab(mh_tab, "Help")
#end def tab_main_help #end def tab_main_help
def tab_table_viewer(self, widget, data): def tab_table_viewer(self, widget, data=None):
"""opens a table viewer tab""" """opens a table viewer tab"""
#print "start of tab_table_viewer" #print "start of tab_table_viewer"
new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings) new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings)
@ -361,7 +447,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.add_and_display_tab(tv_tab, "Table Viewer") self.add_and_display_tab(tv_tab, "Table Viewer")
#end def tab_table_viewer #end def tab_table_viewer
def tabGraphViewer(self, widget, data): def tabGraphViewer(self, widget, data=None):
"""opens a graph viewer tab""" """opens a graph viewer tab"""
#print "start of tabGraphViewer" #print "start of tabGraphViewer"
new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config) new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config)
@ -381,49 +467,9 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.window.connect("destroy", self.destroy) self.window.connect("destroy", self.destroy)
self.window.set_title("Free Poker DB - v%s or higher" % (VERSION, )) self.window.set_title("Free Poker DB - v%s or higher" % (VERSION, ))
self.window.set_border_width(1) self.window.set_border_width(1)
self.window.set_size_request(1020,400) self.window.set_default_size(900,720)
self.window.set_resizable(True) self.window.set_resizable(True)
self.menu_items = (
( "/_Main", None, None, 0, "<Branch>" ),
( "/Main/_Load Profile (broken)", "<control>L", self.dia_load_profile, 0, None ),
( "/Main/_Edit Profile (todo)", "<control>E", self.dia_edit_profile, 0, None ),
( "/Main/_Save Profile (todo)", None, self.dia_save_profile, 0, None ),
("/Main/sep1", None, None, 0, "<Separator>" ),
("/Main/_Quit", "<control>Q", self.quit, 0, None ),
("/_Import", None, None, 0, "<Branch>" ),
("/Import/_Bulk Import", "<control>B", self.tab_bulk_import, 0, None ),
("/Import/_Auto Import and HUD", "<control>A", self.tab_auto_import, 0, None ),
("/Import/Auto _Rating (todo)", "<control>R", self.not_implemented, 0, None ),
("/_Viewers", None, None, 0, "<Branch>" ),
("/_Viewers/_Auto Import and HUD", "<control>A", self.tab_auto_import, 0, None ),
("/Viewers/_Graphs", "<control>G", self.tabGraphViewer, 0, None ),
("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ),
("/Viewers/Player _Details (todo)", None, self.not_implemented, 0, None ),
("/Viewers/_Player Stats (tabulated view)", None, self.tab_player_stats, 0, None ),
("/Viewers/Positional Stats (tabulated view)", None, self.tab_positional_stats, 0, None ),
("/Viewers/Starting _Hands (todo)", None, self.not_implemented, 0, None ),
("/Viewers/_Session Replayer (todo)", None, self.not_implemented, 0, None ),
("/Viewers/Poker_table Viewer (mostly obselete)", "<control>T", self.tab_table_viewer, 0, None ),
#( "/Viewers/Tourney Replayer
( "/_Database", None, None, 0, "<Branch>" ),
( "/Database/Create or Delete _Database (todo)", None, self.dia_create_del_database, 0, None ),
( "/Database/Create or Delete _User (todo)", None, self.dia_create_del_user, 0, None ),
( "/Database/Create or Recreate _Tables", None, self.dia_recreate_tables, 0, None ),
( "/Database/_Statistics (todo)", None, self.dia_database_stats, 0, None ),
( "/D_ebugging", None, None, 0, "<Branch>" ),
( "/Debugging/_Delete Parts of Database (todo)", None, self.dia_delete_db_parts, 0, None ),
( "/Debugging/_Export DB (todo)", None, self.dia_export_db, 0, None ),
( "/Debugging/_Import DB (todo)", None, self.dia_import_db, 0, None ),
( "/Debugging/_Regression test (todo)", None, self.dia_regression_test, 0, None ),
( "/_Help", None, None, 0, "<LastBranch>" ),
( "/Help/_Main Help", "<control>H", self.tab_main_help, 0, None ),
( "/Help/_Abbrevations (todo)", None, self.tab_abbreviations, 0, None ),
( "/Help/sep1", None, None, 0, "<Separator>" ),
( "/Help/A_bout (todo)", None, self.dia_about, 0, None ),
( "/Help/_License and Copying (todo)", None, self.dia_licensing, 0, None )
)
self.main_vbox = gtk.VBox(False, 1) self.main_vbox = gtk.VBox(False, 1)
self.main_vbox.set_border_width(1) self.main_vbox.set_border_width(1)
self.window.add(self.main_vbox) self.window.add(self.main_vbox)
@ -437,7 +483,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.tabs=[] self.tabs=[]
self.tab_names=[] self.tab_names=[]
self.tab_buttons=[] self.tab_buttons=[]
self.tab_box = gtk.HBox(False,1) self.tab_box = gtk.HBox(True,1)
self.main_vbox.pack_start(self.tab_box, False, True, 0) self.main_vbox.pack_start(self.tab_box, False, True, 0)
self.tab_box.show() self.tab_box.show()
#done tab bar #done tab bar
@ -449,11 +495,12 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.tab_main_help(None, None) self.tab_main_help(None, None)
self.status_bar = gtk.Label("Status: Connected to "+self.db.get_backend_name()+" database named "+self.db.database+" on host "+self.db.host) self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.database, self.db.host))
self.main_vbox.pack_end(self.status_bar, False, True, 0) self.main_vbox.pack_end(self.status_bar, False, True, 0)
self.status_bar.show() self.status_bar.show()
self.window.show() self.window.show()
sys.stderr.write("fpdb starting ...")
#end def __init__ #end def __init__
def main(self): def main(self):

View File

@ -59,22 +59,33 @@ class fpdb_db:
self.database=database self.database=database
if backend==self.MYSQL_INNODB: if backend==self.MYSQL_INNODB:
import MySQLdb import MySQLdb
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True) try:
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
except:
raise fpdb_simple.FpdbError("MySQL connection failed")
elif backend==self.PGSQL: elif backend==self.PGSQL:
import psycopg2 import psycopg2
import psycopg2.extensions
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
# If DB connection is made over TCP, then the variables # If DB connection is made over TCP, then the variables
# host, user and password are required # host, user and password are required
print "host=%s user=%s pass=%s." % (host, user, password) print "host=%s user=%s pass=%s." % (host, user, password)
if self.host and self.user and self.password: if self.host and self.user and self.password:
self.db = psycopg2.connect(host = host, try:
user = user, self.db = psycopg2.connect(host = host,
password = password, user = user,
database = database) password = password,
database = database)
except:
raise fpdb_simple.FpdbError("PostgreSQL connection failed")
# For local domain-socket connections, only DB name is # For local domain-socket connections, only DB name is
# needed, and everything else is in fact undefined and/or # needed, and everything else is in fact undefined and/or
# flat out wrong # flat out wrong
else: else:
self.db = psycopg2.connect(database = database) try:
self.db = psycopg2.connect(database = database)
except:
raise fpdb_simple.FpdbError("PostgreSQL connection failed")
else: else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor() self.cursor=self.db.cursor()
@ -136,7 +147,7 @@ class fpdb_db:
if(self.get_backend_name() == 'MySQL InnoDB'): if(self.get_backend_name() == 'MySQL InnoDB'):
#Databases with FOREIGN KEY support need this switched of before you can drop tables #Databases with FOREIGN KEY support need this switched of before you can drop tables
self.drop_referencial_integrity() self.drop_referential_integrity()
# Query the DB to see what tables exist # Query the DB to see what tables exist
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
@ -155,7 +166,7 @@ class fpdb_db:
self.db.commit() self.db.commit()
#end def drop_tables #end def drop_tables
def drop_referencial_integrity(self): def drop_referential_integrity(self):
"""Update all tables to remove foreign keys""" """Update all tables to remove foreign keys"""
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
@ -173,7 +184,7 @@ class fpdb_db:
key = "`" + inner[j][0] + "_" + m.group() + "`" key = "`" + inner[j][0] + "_" + m.group() + "`"
self.cursor.execute("ALTER TABLE " + inner[j][0] + " DROP FOREIGN KEY " + key) self.cursor.execute("ALTER TABLE " + inner[j][0] + " DROP FOREIGN KEY " + key)
self.db.commit() self.db.commit()
#end drop_referencial_inegrity #end drop_referential_inegrity
def get_backend_name(self): def get_backend_name(self):
"""Returns the name of the currently used backend""" """Returns the name of the currently used backend"""

View File

@ -58,13 +58,14 @@ class Importer:
self.cursor = None self.cursor = None
self.filelist = {} self.filelist = {}
self.dirlist = {} self.dirlist = {}
self.siteIds = {}
self.addToDirList = {} self.addToDirList = {}
self.removeFromFileList = {} # to remove deleted files self.removeFromFileList = {} # to remove deleted files
self.monitor = False self.monitor = False
self.updated = {} #Time last import was run {file:mtime} self.updated = {} #Time last import was run {file:mtime}
self.lines = None self.lines = None
self.faobs = None #File as one big string self.faobs = None # File as one big string
self.pos_in_file = {} # dict to remember how far we have read in the file self.pos_in_file = {} # dict to remember how far we have read in the file
#Set defaults #Set defaults
self.callHud = self.config.get_import_parameters().get("callFpdbHud") self.callHud = self.config.get_import_parameters().get("callFpdbHud")
@ -73,6 +74,7 @@ class Importer:
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.fdb.do_connect(self.config) self.fdb.do_connect(self.config)
self.fdb.db.rollback()
#Set functions #Set functions
def setCallHud(self, value): def setCallHud(self, value):
@ -109,20 +111,32 @@ class Importer:
def addImportFile(self, filename, site = "default", filter = "passthrough"): def addImportFile(self, filename, site = "default", filter = "passthrough"):
#TODO: test it is a valid file -> put that in config!! #TODO: test it is a valid file -> put that in config!!
self.filelist[filename] = [site] + [filter] self.filelist[filename] = [site] + [filter]
if site not in self.siteIds:
# Get id from Sites table in DB
self.fdb.cursor.execute(self.fdb.sql.query['getSiteId'], (site,))
result = self.fdb.cursor.fetchall()
if len(result) == 1:
self.siteIds[site] = result[0][0]
else:
if len(result) == 0:
print "[ERROR] Database ID for %s not found" % site
else:
print "[ERROR] More than 1 Database ID found for %s - Multiple currencies not implemented yet" % site
# Called from GuiBulkImport to add a file or directory. # Called from GuiBulkImport to add a file or directory.
def addBulkImportImportFileOrDir(self, inputPath,filter = "passthrough"): def addBulkImportImportFileOrDir(self, inputPath, site = "PokerStars"):
"""Add a file or directory for bulk import""" """Add a file or directory for bulk import"""
filter = self.config.hhcs[site].converter
# Bulk import never monitors # Bulk import never monitors
# if directory, add all files in it. Otherwise add single file. # if directory, add all files in it. Otherwise add single file.
# TODO: only add sane files? # TODO: only add sane files?
if os.path.isdir(inputPath): if os.path.isdir(inputPath):
for subdir in os.walk(inputPath): for subdir in os.walk(inputPath):
for file in subdir[2]: for file in subdir[2]:
self.addImportFile(os.path.join(inputPath, subdir[0], file), site="default", filter=filter) self.addImportFile(os.path.join(inputPath, subdir[0], file), site=site, filter=filter)
else: else:
self.addImportFile(inputPath, site="default", filter=filter) self.addImportFile(inputPath, site=site, filter=filter)
#Add a directory of files to filelist #Add a directory of files to filelist
#Only one import directory per site supported. #Only one import directory per site supported.
#dirlist is a hash of lists: #dirlist is a hash of lists:
@ -212,7 +226,7 @@ class Importer:
#if os.path.isdir(file): #if os.path.isdir(file):
#self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) #self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
else: else:
removeFromFileList[file] = True self.removeFromFileList[file] = True
self.addToDirList = filter(lambda x: self.addImportDirectory(x, True, self.addToDirList[x][0], self.addToDirList[x][1]), self.addToDirList) self.addToDirList = filter(lambda x: self.addImportDirectory(x, True, self.addToDirList[x][0], self.addToDirList[x][1]), self.addToDirList)
for file in self.removeFromFileList: for file in self.removeFromFileList:
@ -221,6 +235,8 @@ class Importer:
self.addToDirList = {} self.addToDirList = {}
self.removeFromFileList = {} self.removeFromFileList = {}
self.fdb.db.rollback()
# This is now an internal function that should not be called directly. # This is now an internal function that should not be called directly.
def import_file_dict(self, file, site, filter): def import_file_dict(self, file, site, filter):
@ -294,10 +310,9 @@ class Importer:
print "TODO: implement importing tournament summaries" print "TODO: implement importing tournament summaries"
#self.faobs = readfile(inputFile) #self.faobs = readfile(inputFile)
#self.parseTourneyHistory() #self.parseTourneyHistory()
return 0 return (0,0,0,1,0)
site = fpdb_simple.recogniseSite(firstline) category=fpdb_simple.recogniseCategory(firstline)
category = fpdb_simple.recogniseCategory(firstline)
startpos = 0 startpos = 0
stored = 0 #counter stored = 0 #counter
@ -305,59 +320,28 @@ class Importer:
partial = 0 #counter partial = 0 #counter
errors = 0 #counter errors = 0 #counter
for i in xrange (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method for i in xrange (len(self.lines)):
if len(self.lines[i]) < 2: if (len(self.lines[i])<2): #Wierd way to detect for '\r\n' or '\n'
endpos = i endpos=i
hand = self.lines[startpos:endpos] hand=self.lines[startpos:endpos]
if len(hand[0]) < 2: if (len(hand[0])<2):
hand = hand[1:] hand=hand[1:]
cancelled=False
damaged=False
if (site=="ftp"):
for i in range (len(hand)):
if hand[i].endswith(" has been canceled"): #this is their typo. this is a typo, right?
cancelled = True
#FTP generates lines looking like: if (len(hand)<3):
#Seat 1: IOS Seat 2: kashman59 (big blind) showed [8c 9d] and won ($3.25) with a pair of Eights
#ie. Seat X multiple times on the same line in the summary section, when a new player sits down in the
#middle of the hand.
#TODO: Deal with this properly, either fix the file or make the parsing code work with this line.
if "Seat" in hand[i]:
mo = re.search(" Seat [0-9]+: ", hand[i])
if mo:
print "mo=", mo, "\nmo.start=", mo.start(),"\nhand[i]=",hand[i]
hand.insert(i+1, hand[i][mo.start()+1:])
hand[i] = hand[i][0:mo.start()]
if len(hand) < 3:
pass pass
#todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work. #TODO: This is ugly - we didn't actually find the start of the
elif hand[0].endswith(" (partial)"): #partial hand - do nothing # hand with the outer loop so we test again...
partial += 1 else:
elif "Seat" not in hand[1] and "Seat" not in hand[2] and "Seat" not in hand[3]: isTourney=fpdb_simple.isTourney(hand[0])
partial += 1
elif cancelled or damaged:
partial += 1
if damaged:
print """
DEBUG: Partial hand triggered by a line containing 'Seat X:' twice. This is a
bug in the FTP software when a player sits down in the middle of a hand.
Adding a newline after the player name will fix the issue
"""
print "File: %s" %(file)
print "Line: %s" %(startpos)
else: #normal processing
isTourney = fpdb_simple.isTourney(hand[0])
if not isTourney: if not isTourney:
hand = fpdb_simple.filterAnteBlindFold(site,hand) hand = fpdb_simple.filterAnteBlindFold(hand)
self.hand=hand self.hand=hand
try: try:
handsId = fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db handsId = fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db
,self.fdb.cursor, site, category, hand, self.config) ,self.fdb.cursor, self.siteIds[site], category, hand, self.config)
self.fdb.db.commit() self.fdb.db.commit()
stored += 1 stored += 1
@ -367,6 +351,7 @@ class Importer:
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
except fpdb_simple.DuplicateError: except fpdb_simple.DuplicateError:
duplicates += 1 duplicates += 1
self.fdb.db.rollback()
except (ValueError), fe: except (ValueError), fe:
errors += 1 errors += 1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
@ -374,11 +359,12 @@ class Importer:
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.fdb.db.commit() #dont remove this, in case hand processing was cancelled. self.fdb.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
else:
self.fdb.db.rollback()
except (fpdb_simple.FpdbError), fe: except (fpdb_simple.FpdbError), fe:
errors += 1 errors += 1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
#fe.printStackTrace() #todo: get stacktrace
self.fdb.db.rollback() self.fdb.db.rollback()
if self.settings['failOnError']: if self.settings['failOnError']:
@ -386,18 +372,18 @@ class Importer:
raise raise
if self.settings['minPrint']: if self.settings['minPrint']:
if not ((stored+duplicates+partial+errors) % self.settings['minPrint']): if not ((stored+duplicates+errors) % self.settings['minPrint']):
print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors print "stored:", stored, "duplicates:", duplicates, "errors:", errors
if self.settings['handCount']: if self.settings['handCount']:
if ((stored+duplicates+partial+errors) >= self.settings['handCount']): if ((stored+duplicates+errors) >= self.settings['handCount']):
if not self.settings['quiet']: if not self.settings['quiet']:
print "quitting due to reaching the amount of hands to be imported" print "quitting due to reaching the amount of hands to be imported"
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime) print "Total stored:", stored, "duplicates:", duplicates, "errors:", errors, " time:", (time() - starttime)
sys.exit(0) sys.exit(0)
startpos = endpos startpos = endpos
ttime = time() - starttime ttime = time() - starttime
print "\rTotal stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", ttime print "\rTotal stored:", stored, "duplicates:", duplicates, "errors:", errors, " time:", ttime
if not stored: if not stored:
if duplicates: if duplicates:
@ -408,16 +394,11 @@ class Importer:
else: else:
print "failed to read a single hand from file:", inputFile print "failed to read a single hand from file:", inputFile
handsId=0 handsId=0
#todo: this will cause return of an unstored hand number if the last hand was error or partial #todo: this will cause return of an unstored hand number if the last hand was error
self.fdb.db.commit() self.fdb.db.commit()
self.handsId=handsId self.handsId=handsId
return (stored, duplicates, partial, errors, ttime) return (stored, duplicates, partial, errors, ttime)
def parseTourneyHistory(self):
print "Tourney history parser stub"
#Find tournament boundaries.
#print self.foabs
def printEmailErrorMessage(self, errors, filename, line): def printEmailErrorMessage(self, errors, filename, line):
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it." print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."

View File

@ -21,7 +21,7 @@ import fpdb_simple
import fpdb_save_to_db import fpdb_save_to_db
#parses a holdem hand #parses a holdem hand
def mainParser(backend, db, cursor, site, category, hand, config): def mainParser(backend, db, cursor, siteID, category, hand, config):
category = fpdb_simple.recogniseCategory(hand[0]) category = fpdb_simple.recogniseCategory(hand[0])
base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud" base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud"
@ -34,9 +34,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
#part 1: read hand no and check for duplicate #part 1: read hand no and check for duplicate
siteHandNo = fpdb_simple.parseSiteHandNo(hand[0]) siteHandNo = fpdb_simple.parseSiteHandNo(hand[0])
handStartTime = fpdb_simple.parseHandStartTime(hand[0], site) handStartTime = fpdb_simple.parseHandStartTime(hand[0])
siteID = fpdb_simple.recogniseSiteID(cursor, site)
#print "parse logic, siteID:",siteID,"site:",site
isTourney = fpdb_simple.isTourney(hand[0]) isTourney = fpdb_simple.isTourney(hand[0])
smallBlindLine = 0 smallBlindLine = 0
@ -49,8 +47,6 @@ def mainParser(backend, db, cursor, site, category, hand, config):
gametypeID = fpdb_simple.recogniseGametypeID(backend, db, cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney) gametypeID = fpdb_simple.recogniseGametypeID(backend, db, cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney)
if isTourney: if isTourney:
if site != "ps":
raise fpdb_simple.FpdbError("tourneys are only supported on PS right now")
siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0]) siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0])
buyin = fpdb_simple.parseBuyin(hand[0]) buyin = fpdb_simple.parseBuyin(hand[0])
fee = fpdb_simple.parseFee(hand[0]) fee = fpdb_simple.parseFee(hand[0])
@ -64,7 +60,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo) fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo)
hand = fpdb_simple.filterCrap(site, hand, isTourney) hand = fpdb_simple.filterCrap(hand, isTourney)
#part 2: classify lines by type (e.g. cards, action, win, sectionchange) and street #part 2: classify lines by type (e.g. cards, action, win, sectionchange) and street
fpdb_simple.classifyLines(hand, category, lineTypes, lineStreets) fpdb_simple.classifyLines(hand, category, lineTypes, lineStreets)
@ -77,7 +73,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
names = fpdb_simple.parseNames(seatLines) names = fpdb_simple.parseNames(seatLines)
playerIDs = fpdb_simple.recognisePlayerIDs(cursor, names, siteID) playerIDs = fpdb_simple.recognisePlayerIDs(cursor, names, siteID)
tmp = fpdb_simple.parseCashesAndSeatNos(seatLines, site) tmp = fpdb_simple.parseCashesAndSeatNos(seatLines)
startCashes = tmp['startCashes'] startCashes = tmp['startCashes']
seatNos = tmp['seatNos'] seatNos = tmp['seatNos']
@ -90,30 +86,27 @@ def mainParser(backend, db, cursor, site, category, hand, config):
#part 4: take appropriate action for each line based on linetype #part 4: take appropriate action for each line based on linetype
for i, line in enumerate(hand): for i, line in enumerate(hand):
if lineTypes[i] == "cards": if lineTypes[i] == "cards":
fpdb_simple.parseCardLine(site, category, lineStreets[i], line, names, cardValues, cardSuits, boardValues, boardSuits) fpdb_simple.parseCardLine(category, lineStreets[i], line, names, cardValues, cardSuits, boardValues, boardSuits)
#if category=="studhilo": #if category=="studhilo":
# print "hand[i]:", hand[i] # print "hand[i]:", hand[i]
# print "cardValues:", cardValues # print "cardValues:", cardValues
# print "cardSuits:", cardSuits # print "cardSuits:", cardSuits
elif lineTypes[i] == "action": elif lineTypes[i] == "action":
fpdb_simple.parseActionLine(site, base, isTourney, line, lineStreets[i], playerIDs, names, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo) fpdb_simple.parseActionLine(base, isTourney, line, lineStreets[i], playerIDs, names, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo)
elif lineTypes[i] == "win": elif lineTypes[i] == "win":
fpdb_simple.parseWinLine(line, site, names, winnings, isTourney) fpdb_simple.parseWinLine(line, names, winnings, isTourney)
elif lineTypes[i] == "rake": elif lineTypes[i] == "rake":
totalRake = 0 if isTourney else fpdb_simple.parseRake(line) totalRake = 0 if isTourney else fpdb_simple.parseRake(line)
fpdb_simple.splitRake(winnings, rakes, totalRake) fpdb_simple.splitRake(winnings, rakes, totalRake)
elif lineTypes[i]=="header" or lineTypes[i]=="rake" or lineTypes[i]=="name" or lineTypes[i]=="ignore": elif lineTypes[i]=="header" or lineTypes[i]=="rake" or lineTypes[i]=="name" or lineTypes[i]=="ignore":
pass pass
elif lineTypes[i]=="ante": elif lineTypes[i]=="ante":
fpdb_simple.parseAnteLine(line, site, isTourney, names, antes) fpdb_simple.parseAnteLine(line, isTourney, names, antes)
elif lineTypes[i]=="table": elif lineTypes[i]=="table":
tableResult=fpdb_simple.parseTableLine(site, base, line) tableResult=fpdb_simple.parseTableLine(base, line)
else: else:
raise fpdb_simple.FpdbError("unrecognised lineType:"+lineTypes[i]) raise fpdb_simple.FpdbError("unrecognised lineType:"+lineTypes[i])
if site == "ftp":
tableResult = fpdb_simple.parseTableLine(site, base, hand[0])
maxSeats = tableResult['maxSeats'] maxSeats = tableResult['maxSeats']
tableName = tableResult['tableName'] tableName = tableResult['tableName']
#print "before part5, antes:", antes #print "before part5, antes:", antes
@ -128,7 +121,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, )) cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
limit_type = cursor.fetchone()[0] limit_type = cursor.fetchone()[0]
fpdb_simple.convert3B4B(site, category, limit_type, actionTypes, actionAmounts) fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts)
totalWinnings = sum(winnings) totalWinnings = sum(winnings)
@ -165,7 +158,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
, allIns, actionAmounts, actionNos, hudImportData, maxSeats , allIns, actionAmounts, actionNos, hudImportData, maxSeats
, tableName, seatNos) , tableName, seatNos)
else: else:
raise fpdb_simple.FpdbError("unrecognised category") # it's impossible to get here, but w/e raise fpdb_simple.FpdbError("unrecognised category")
else: else:
if base == "hold": if base == "hold":
result = fpdb_save_to_db.ring_holdem_omaha( result = fpdb_save_to_db.ring_holdem_omaha(
@ -183,7 +176,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
, actionAmounts, actionNos, hudImportData, maxSeats, tableName , actionAmounts, actionNos, hudImportData, maxSeats, tableName
, seatNos) , seatNos)
else: else:
raise fpdb_simple.FpdbError ("unrecognised category") # also impossible to get here raise fpdb_simple.FpdbError ("unrecognised category")
db.commit() db.commit()
return result return result
#end def mainParser #end def mainParser

View File

@ -32,8 +32,7 @@ saveActions = True # set this to False to avoid storing action data
# Pros: speeds up imports # Pros: speeds up imports
# Cons: no action data is saved, so you need to keep the hand histories # Cons: no action data is saved, so you need to keep the hand histories
# variance not available on stats page # variance not available on stats page
# no graphs # : No graphs
#stores a stud/razz hand into the database #stores a stud/razz hand into the database
def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes ,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
@ -55,7 +54,7 @@ def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametyp
,start_cashes, antes, card_values ,start_cashes, antes, card_values
,card_suits, winnings, rakes, seatNos) ,card_suits, winnings, rakes, seatNos)
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
if saveActions: if saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types fpdb_simple.storeActions(cursor, hands_players_ids, action_types
@ -83,17 +82,17 @@ def ring_holdem_omaha(config, backend, db, cursor, base, category, site_hand_no,
t2 = time() t2 = time()
hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats) ,hand_start_time, names, tableName, maxSeats, hudImportData)
t3 = time() t3 = time()
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha( hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha(
backend, db, cursor, category, hands_id, player_ids, start_cashes backend, db, cursor, category, hands_id, player_ids, start_cashes
, positions, card_values, card_suits, winnings, rakes, seatNos) , positions, card_values, card_suits, winnings, rakes, seatNos, hudImportData)
t4 = time() t4 = time()
#print "ring holdem, backend=%d" % backend #print "ring holdem, backend=%d" % backend
if fastStoreHudCache: if fastStoreHudCache:
fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, player_ids, hudImportData) fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
else: else:
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
t5 = time() t5 = time()
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
t6 = time() t6 = time()
@ -132,9 +131,9 @@ def tourney_holdem_omaha(config, backend, db, cursor, base, category, siteTourne
#print "tourney holdem, backend=%d" % backend #print "tourney holdem, backend=%d" % backend
if fastStoreHudCache: if fastStoreHudCache:
fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, player_ids, hudImportData) fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
else: else:
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
@ -166,7 +165,7 @@ def tourney_stud(config, backend, db, cursor, base, category, siteTourneyNo, buy
, playerIds, startCashes, antes, cardValues, cardSuits , playerIds, startCashes, antes, cardValues, cardSuits
, winnings, rakes, seatNos, tourneys_players_ids) , winnings, rakes, seatNos, tourneys_players_ids)
fpdb_simple.storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData) fpdb_simple.storeHudCache(backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
if saveActions: if saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos) fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)

File diff suppressed because it is too large Load Diff