Merge branch 'master' of git://git.assembla.com/fpdboz.git
This commit is contained in:
commit
2e4c792bbe
83
pyfpdb/Card.py
Executable file
83
pyfpdb/Card.py
Executable 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] )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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,14 +223,22 @@ class Database:
|
||||||
return winners
|
return winners
|
||||||
|
|
||||||
def get_stats_from_hand(self, hand, aggregate = False):
|
def get_stats_from_hand(self, hand, aggregate = False):
|
||||||
c = self.connection.cursor()
|
if self.hud_style == 'S':
|
||||||
|
return( self.get_stats_from_hand_session(hand) )
|
||||||
|
else: # self.hud_style == A
|
||||||
if aggregate:
|
if aggregate:
|
||||||
query = 'get_stats_from_hand_aggregated'
|
query = 'get_stats_from_hand_aggregated'
|
||||||
subs = (hand, hand, hand)
|
|
||||||
else:
|
else:
|
||||||
query = 'get_stats_from_hand'
|
query = 'get_stats_from_hand'
|
||||||
subs = (hand, 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()
|
||||||
|
|
||||||
# now get the stats
|
# now get the stats
|
||||||
c.execute(self.sql.query[query], subs)
|
c.execute(self.sql.query[query], subs)
|
||||||
|
@ -200,8 +247,55 @@ class Database:
|
||||||
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()
|
||||||
|
if row:
|
||||||
return row[0]
|
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')
|
||||||
|
if hero:
|
||||||
print "nutOmatic is id_player = %d" % 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
|
||||||
|
|
|
@ -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
540
pyfpdb/Filters.py
Normal 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
|
@ -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.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 = 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__":
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
tmp = self.sql.query['playerStatsByPosition']
|
siteids = self.filters.getSiteIds()
|
||||||
|
limits = self.filters.getLimits()
|
||||||
|
seats = self.filters.getSeats()
|
||||||
|
dates = self.filters.getDates()
|
||||||
|
sitenos = []
|
||||||
|
playerids = []
|
||||||
|
|
||||||
result = self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[self.activesite],))
|
# Which sites are selected?
|
||||||
result = self.cursor.fetchall()
|
for site in sites:
|
||||||
if not result == ():
|
if sites[site] == True:
|
||||||
pid = result[0][0]
|
sitenos.append(siteids[site])
|
||||||
pid = result[0][0]
|
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
||||||
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
|
result = self.db.cursor.fetchall()
|
||||||
self.cursor.execute(tmp)
|
if len(result) == 1:
|
||||||
result = self.cursor.fetchall()
|
playerids.append(result[0][0])
|
||||||
cols = 16
|
|
||||||
rows = len(result)+1 # +1 for title row
|
if not sitenos:
|
||||||
self.stats_table = gtk.Table(rows, cols, False)
|
#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.set_col_spacings(4)
|
||||||
self.stats_table.show()
|
self.stats_table.show()
|
||||||
vbox.add(self.stats_table)
|
vbox.add(self.stats_table)
|
||||||
|
|
||||||
# Create header row
|
|
||||||
titles = ("Game", "Position", "#", "VPIP", "PFR", "Saw_F", "SawSD", "WtSDwsF", "W$SD", "FlAFq", "TuAFq", "RvAFq", "PoFAFq", "Net($)", "BB/100", "$/hand", "Variance")
|
|
||||||
|
|
||||||
col = 0
|
|
||||||
row = 0
|
row = 0
|
||||||
for t in titles:
|
col = 0
|
||||||
l = gtk.Label(titles[col])
|
for t in self.posnheads:
|
||||||
|
l = gtk.Label(self.posnheads[col])
|
||||||
l.show()
|
l.show()
|
||||||
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
|
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
|
||||||
col +=1
|
col +=1
|
||||||
|
|
||||||
for row in range(rows-1):
|
tmp = self.sql.query['playerStatsByPosition']
|
||||||
|
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]
|
||||||
|
|
||||||
|
last_game,last_seats,sqlrow = "","",0
|
||||||
|
while sqlrow < rows:
|
||||||
if(row%2 == 0):
|
if(row%2 == 0):
|
||||||
bgcolor = "white"
|
bgcolor = "white"
|
||||||
else:
|
else:
|
||||||
bgcolor = "lightgrey"
|
bgcolor = "lightgrey"
|
||||||
for col in range(cols):
|
rowprinted=0
|
||||||
|
avgcol = colnames.index('avgseats')
|
||||||
|
for col,colname in enumerate(self.posncols):
|
||||||
|
if colname in colnames:
|
||||||
|
sqlcol = colnames.index(colname)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
eb = gtk.EventBox()
|
eb = gtk.EventBox()
|
||||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
||||||
if result[row][col]:
|
# print blank row between levels:
|
||||||
l = gtk.Label(result[row][col])
|
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:
|
||||||
|
l = gtk.Label(result[sqlrow][sqlcol])
|
||||||
|
rowprinted=1
|
||||||
else:
|
else:
|
||||||
l = gtk.Label(' ')
|
l = gtk.Label(' ')
|
||||||
if col == 0:
|
if col == 0:
|
||||||
l.set_alignment(xalign=0.0, yalign=0.5)
|
l.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
|
elif col == 1:
|
||||||
|
l.set_alignment(xalign=0.5, yalign=0.5)
|
||||||
else:
|
else:
|
||||||
l.set_alignment(xalign=1.0, yalign=0.5)
|
l.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
eb.add(l)
|
eb.add(l)
|
||||||
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
||||||
l.show()
|
l.show()
|
||||||
eb.show()
|
eb.show()
|
||||||
self.fdb.db.commit()
|
last_game = result[sqlrow][0]
|
||||||
|
last_seats = result[sqlrow][avgcol]
|
||||||
|
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:
|
else:
|
||||||
suit = "s"
|
query = query.replace("<player_test>", "1 = 2")
|
||||||
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 seats:
|
||||||
if(self.buttongroup == None):
|
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
|
||||||
button = gtk.RadioButton(None, site + " id:")
|
if 'show' in seats and seats['show']:
|
||||||
button.set_active(True)
|
query = query.replace('<groupbyseats>', ',hc.activeSeats')
|
||||||
self.buttongroup = button
|
query = query.replace('<orderbyseats>', ',stats.AvgSeats')
|
||||||
self.activesite = site
|
|
||||||
else:
|
else:
|
||||||
button = gtk.RadioButton(self.buttongroup, site + " id:")
|
query = query.replace('<groupbyseats>', '')
|
||||||
hbox.pack_start(button, True, True, 0)
|
query = query.replace('<orderbyseats>', '')
|
||||||
button.connect("toggled", self.toggleCallback, site)
|
else:
|
||||||
button.show()
|
query = query.replace('<seats_test>', 'between 0 and 100')
|
||||||
|
query = query.replace('<groupbyseats>', '')
|
||||||
|
query = query.replace('<orderbyseats>', '')
|
||||||
|
|
||||||
pname = gtk.Entry()
|
if [x for x in limits if str(x).isdigit()]:
|
||||||
pname.set_text(player)
|
blindtest = str(tuple([x for x in limits if str(x).isdigit()]))
|
||||||
pname.set_width_chars(20)
|
blindtest = blindtest.replace("L", "")
|
||||||
hbox.pack_start(pname, False, True, 0)
|
blindtest = blindtest.replace(",)",")")
|
||||||
pname.connect("changed", self.__set_hero_name, site)
|
query = query.replace("<gtbigBlind_test>", "gt.bigBlind in " + blindtest)
|
||||||
#TODO: Look at GtkCompletion - to fill out usernames
|
else:
|
||||||
pname.show()
|
query = query.replace("<gtbigBlind_test>", "gt.bigBlind = -1 ")
|
||||||
self.__set_hero_name(pname, site)
|
|
||||||
|
|
||||||
def __set_hero_name(self, w, site):
|
groupLevels = "show" not in str(limits)
|
||||||
self.heroes[site] = w.get_text()
|
if groupLevels:
|
||||||
|
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")
|
||||||
|
|
||||||
def __init__(self, db, config, querylist, debug=True):
|
# Filter on dates
|
||||||
self.debug=debug
|
query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
|
||||||
self.conf=config
|
|
||||||
|
|
||||||
# create new db connection to avoid conflicts with other threads
|
|
||||||
self.fdb = fpdb_db.fpdb_db()
|
|
||||||
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
310
pyfpdb/GuiSessionViewer.py
Normal 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()
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
if table in self.hud_dict:
|
||||||
self.hud_dict[table].kill()
|
self.hud_dict[table].kill()
|
||||||
self.hud_dict[table].main_window.destroy()
|
self.hud_dict[table].main_window.destroy()
|
||||||
self.vb.remove(self.hud_dict[table].tablehudlabel)
|
self.vb.remove(self.hud_dict[table].tablehudlabel)
|
||||||
del(self.hud_dict[table])
|
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")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
478
pyfpdb/SQL.py
478
pyfpdb/SQL.py
|
@ -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,147 +172,327 @@ 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)
|
||||||
|
INNER JOIN HudCache hc ON (hc.playerId = hp.playerId)
|
||||||
|
INNER JOIN Players p ON (p.id = hc.playerId)
|
||||||
|
WHERE h.id = %s
|
||||||
|
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 > %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
|
WHERE gt1.siteid = gt2.siteid
|
||||||
AND gt1.type = gt2.type
|
AND gt1.type = gt2.type
|
||||||
AND gt1.category = gt2.category
|
AND gt1.category = gt2.category
|
||||||
AND gt1.limittype = gt2.limittype
|
AND gt1.limittype = gt2.limittype
|
||||||
AND gt2.id = Hands.gametypeId
|
AND gt2.id = h.gametypeId)
|
||||||
AND Hands.id = %s)
|
GROUP BY hc.PlayerId, p.name, hc.styleKey
|
||||||
GROUP BY HudCache.PlayerId
|
"""
|
||||||
|
|
||||||
|
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'] = """
|
||||||
|
@ -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')
|
||||||
|
|
121
pyfpdb/Stats.py
121
pyfpdb/Stats.py
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
195
pyfpdb/fpdb.py
195
pyfpdb/fpdb.py
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
try:
|
||||||
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
|
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:
|
||||||
|
try:
|
||||||
self.db = psycopg2.connect(host = host,
|
self.db = psycopg2.connect(host = host,
|
||||||
user = user,
|
user = user,
|
||||||
password = password,
|
password = password,
|
||||||
database = database)
|
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:
|
||||||
|
try:
|
||||||
self.db = psycopg2.connect(database = database)
|
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"""
|
||||||
|
|
|
@ -58,6 +58,7 @@ 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
|
||||||
|
@ -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,9 +310,8 @@ 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
|
||||||
|
@ -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]:
|
|
||||||
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])
|
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."
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue
Block a user