Merge branch 'master' of git://git.assembla.com/fpdboz
Conflicts: pyfpdb/Database.py pyfpdb/SQL.py pyfpdb/fpdb_db.py pyfpdb/fpdb_save_to_db.py
This commit is contained in:
commit
4ef4260342
35
THANKS.txt
Normal file
35
THANKS.txt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
=**Thanks - Contributors to FPDB**=
|
||||||
|
|
||||||
|
This page is to acknowledge the people developing and contributing to FPDB, be it with code, documentation, testing, forum support and everything else that is required to make an open source project work.
|
||||||
|
|
||||||
|
Most people have found the project though the software forum at [[http://forumserver.twoplustwo.com/45/software/|2+2]] so most references to 2+2 aliases
|
||||||
|
|
||||||
|
Sincere apologies to those not listed. FPDB started life in August 2008, but we only got around to putting together a proper contributers list in April 2009. Private messages and emails have been deleted so we dont have a full history of non-code based contributions
|
||||||
|
|
||||||
|
==**Developers**==
|
||||||
|
Active:
|
||||||
|
Steffen123 - The initial code drop and project lead.
|
||||||
|
Eratosthenes - Initial HUD code drop, release manager
|
||||||
|
Ekdikeo - Contributor - contributed Everleaf support and much HUD work
|
||||||
|
Sorrow - Contributor - Graphing, import framework
|
||||||
|
mcturnbull (2+2?) - Contributor - import framework
|
||||||
|
|
||||||
|
Inactive:
|
||||||
|
Sqlcoder - SQL query guru - Graphing filters, Player Stats, Positional Stats queries + general sql optimisation work
|
||||||
|
Bostik - Author of [[http://bostik.iki.fi/pokerstats/|PokerStats]] - Postgres linux work. PokerStats import code was the inspiration for the new framework. I'm sure well use more of his ideas in future.
|
||||||
|
FIXME - Original windows installer/packager
|
||||||
|
|
||||||
|
==Documentation==
|
||||||
|
FIXME - Original web site guy
|
||||||
|
FIXME - Guy who wrote the Mac install page
|
||||||
|
FIXME - Any other wiki editors.
|
||||||
|
|
||||||
|
==Other==
|
||||||
|
Eleatic Stranger - Super tester and contributor - Our #1 tester. Tracks the git repo and works the code harder than anyone and gives great feedback and bug reports. Thankyou.
|
||||||
|
Xaviax - Tester and honorary helpdesk - Another excellent tester tracking git, has responded to many queries in the fpdb thread on his own time.
|
||||||
|
KayosD - Hand History donation - Carbon Poker
|
||||||
|
freerollerjb - Hand History donation - Carbon Poker
|
||||||
|
puru - Hand History donation - Carbon Poker
|
||||||
|
freestailo - Hand History donation - Carbon Poker
|
||||||
|
MoDDe (Sourceforge) - Hand History Donation - Betfair
|
||||||
|
Jay10826 - Hand History donation - Ultimate Bet
|
|
@ -54,6 +54,7 @@ class CarbonPoker(HandHistoryConverter):
|
||||||
print "Initialising Carbon Poker converter class"
|
print "Initialising Carbon Poker converter class"
|
||||||
HandHistoryConverter.__init__(self, config, filename, "Carbon") # Call super class init
|
HandHistoryConverter.__init__(self, config, filename, "Carbon") # Call super class init
|
||||||
self.setFileType("xml")
|
self.setFileType("xml")
|
||||||
|
self.siteId = 4 # Needs to match id entry in Sites database
|
||||||
|
|
||||||
def readSupportedGames(self):
|
def readSupportedGames(self):
|
||||||
pass
|
pass
|
||||||
|
|
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)
|
||||||
|
@ -706,6 +721,11 @@ if __name__== "__main__":
|
||||||
for w in c.aux_windows.keys():
|
for w in c.aux_windows.keys():
|
||||||
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():
|
||||||
|
|
130
pyfpdb/Database.py
Normal file → Executable file
130
pyfpdb/Database.py
Normal file → Executable file
|
@ -27,12 +27,14 @@ Create and manage the database objects.
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import string
|
import string
|
||||||
|
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):
|
||||||
|
@ -83,8 +85,38 @@ class Database:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
self.type = db_params['db-type']
|
self.type = db_params['db-type']
|
||||||
self.sql = SQL.Sql(game = game, type = self.type)
|
self.db_server = c.supported_databases[db_name].db_server
|
||||||
|
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()
|
||||||
|
|
||||||
|
@ -126,13 +158,19 @@ 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):
|
||||||
|
@ -190,31 +228,89 @@ class Database:
|
||||||
return winners
|
return winners
|
||||||
|
|
||||||
def get_stats_from_hand(self, hand, aggregate = False):
|
def get_stats_from_hand(self, hand, aggregate = False):
|
||||||
|
if self.hud_style == 'S':
|
||||||
|
return( self.get_stats_from_hand_session(hand) )
|
||||||
|
else: # self.hud_style == A
|
||||||
|
if aggregate:
|
||||||
|
query = 'get_stats_from_hand_aggregated'
|
||||||
|
else:
|
||||||
|
query = 'get_stats_from_hand'
|
||||||
|
|
||||||
|
if self.hud_style == 'T':
|
||||||
|
stylekey = self.date_ndays_ago
|
||||||
|
else: # assume A (all-time)
|
||||||
|
stylekey = '0000000' # all stylekey values should be higher than this
|
||||||
|
|
||||||
|
subs = (hand, hand, stylekey)
|
||||||
|
#print "get stats: hud style =", self.hud_style, "subs =", subs
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
|
|
||||||
if aggregate:
|
# now get the stats
|
||||||
query = 'get_stats_from_hand_aggregated'
|
|
||||||
subs = (hand, hand, hand)
|
|
||||||
else:
|
|
||||||
query = 'get_stats_from_hand'
|
|
||||||
subs = (hand, hand)
|
|
||||||
|
|
||||||
# now get the stats
|
|
||||||
c.execute(self.sql.query[query], subs)
|
c.execute(self.sql.query[query], subs)
|
||||||
colnames = [desc[0] for desc in c.description]
|
colnames = [desc[0] for desc in c.description]
|
||||||
stat_dict = {}
|
stat_dict = {}
|
||||||
for row in c.fetchall():
|
for row in c.fetchall():
|
||||||
t_dict = {}
|
t_dict = {}
|
||||||
for name, val in zip(colnames, row):
|
for name, val in zip(colnames, row):
|
||||||
t_dict[name] = val
|
t_dict[name.lower()] = val
|
||||||
|
# print t_dict
|
||||||
stat_dict[t_dict['player_id']] = t_dict
|
stat_dict[t_dict['player_id']] = t_dict
|
||||||
|
|
||||||
|
return stat_dict
|
||||||
|
|
||||||
|
# uses query on handsplayers instead of hudcache to get stats on just this session
|
||||||
|
def get_stats_from_hand_session(self, hand):
|
||||||
|
|
||||||
|
if self.hud_style == 'S':
|
||||||
|
query = self.sql.query['get_stats_from_hand_session']
|
||||||
|
if self.db_server == 'mysql':
|
||||||
|
query = query.replace("<signed>", 'signed ')
|
||||||
|
else:
|
||||||
|
query = query.replace("<signed>", '')
|
||||||
|
else: # self.hud_style == A
|
||||||
|
return None
|
||||||
|
|
||||||
|
subs = (self.hand_1day_ago, hand)
|
||||||
|
c = self.connection.cursor()
|
||||||
|
|
||||||
|
# now get the stats
|
||||||
|
#print "sess_stats: subs =", subs, "subs[0] =", subs[0]
|
||||||
|
c.execute(query, subs)
|
||||||
|
colnames = [desc[0] for desc in c.description]
|
||||||
|
n,stat_dict = 0,{}
|
||||||
|
row = c.fetchone()
|
||||||
|
while row:
|
||||||
|
if colnames[0].lower() == 'player_id':
|
||||||
|
playerid = row[0]
|
||||||
|
else:
|
||||||
|
print "ERROR: query %s result does not have player_id as first column" % (query,)
|
||||||
|
break
|
||||||
|
|
||||||
|
for name, val in zip(colnames, row):
|
||||||
|
if not playerid in stat_dict:
|
||||||
|
stat_dict[playerid] = {}
|
||||||
|
stat_dict[playerid][name.lower()] = val
|
||||||
|
elif not name.lower() in stat_dict[playerid]:
|
||||||
|
stat_dict[playerid][name.lower()] = val
|
||||||
|
elif name.lower() not in ('hand_id', 'player_id', 'seat', 'screen_name', 'seats'):
|
||||||
|
stat_dict[playerid][name.lower()] += val
|
||||||
|
n += 1
|
||||||
|
if n >= 4000: break # todo: don't think this is needed so set nice and high
|
||||||
|
# for now - comment out or remove?
|
||||||
|
row = c.fetchone()
|
||||||
|
#print " %d rows fetched, len(stat_dict) = %d" % (n, len(stat_dict))
|
||||||
|
|
||||||
|
#print "session stat_dict =", stat_dict
|
||||||
return stat_dict
|
return stat_dict
|
||||||
|
|
||||||
def get_player_id(self, config, site, player_name):
|
def get_player_id(self, config, site, player_name):
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site})
|
c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site})
|
||||||
row = c.fetchone()
|
row = c.fetchone()
|
||||||
return row[0]
|
if row:
|
||||||
|
return row[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
c = Configuration.Config()
|
c = Configuration.Config()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -27,7 +27,8 @@ from HandHistoryConverter import *
|
||||||
class Everleaf(HandHistoryConverter):
|
class Everleaf(HandHistoryConverter):
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
re_SplitHands = re.compile(r"(\n\n\n+)")
|
re_SplitHands = re.compile(r"\n\n\n+")
|
||||||
|
re_TailSplitHands = re.compile(r"(\n\n\n+)")
|
||||||
re_GameInfo = re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) ?(?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
|
re_GameInfo = re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) ?(?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
|
||||||
#re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) (?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
|
#re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) (?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
|
||||||
re_HandInfo = re.compile(ur".*#(?P<HID>[0-9]+)\n.*\n(Blinds )?(?:\$| €|)(?P<SB>[.0-9]+)/(?:\$| €|)(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>.+$)", re.MULTILINE)
|
re_HandInfo = re.compile(ur".*#(?P<HID>[0-9]+)\n.*\n(Blinds )?(?:\$| €|)(?P<SB>[.0-9]+)/(?:\$| €|)(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>.+$)", re.MULTILINE)
|
||||||
|
@ -48,6 +49,7 @@ debugging: if False, pass on partially supported game types. If true, have a go
|
||||||
logging.info("Initialising Everleaf converter class")
|
logging.info("Initialising Everleaf converter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
self.siteId = 3 # Needs to match id entry in Sites database
|
||||||
self.debugging = debugging
|
self.debugging = debugging
|
||||||
if autostart:
|
if autostart:
|
||||||
self.start()
|
self.start()
|
||||||
|
|
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
|
@ -28,11 +28,13 @@ class Fulltilt(HandHistoryConverter):
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
re_GameInfo = re.compile('- (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<LIMIT>(No Limit|Pot Limit|Limit))? (?P<GAME>(Hold\'em|Omaha Hi|Razz))')
|
re_GameInfo = re.compile('- (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<LIMIT>(No Limit|Pot Limit|Limit))? (?P<GAME>(Hold\'em|Omaha Hi|Razz))')
|
||||||
re_SplitHands = re.compile(r"(\n\n+)")
|
re_SplitHands = re.compile(r"\n\n+")
|
||||||
|
re_TailSplitHands = re.compile(r"(\n\n+)")
|
||||||
re_HandInfo = re.compile('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)')
|
re_HandInfo = re.compile('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)')
|
||||||
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
||||||
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[.0-9]+)\)\n')
|
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[.0-9]+)\)\n')
|
||||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||||
|
# NB: if we ever match "Full Tilt Poker" we should also match "FullTiltPoker", which PT Stud erroneously exports.
|
||||||
|
|
||||||
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
|
||||||
"""\
|
"""\
|
||||||
|
@ -43,6 +45,7 @@ follow : whether to tail -f the input"""
|
||||||
logging.info("Initialising Fulltilt converter class")
|
logging.info("Initialising Fulltilt converter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
self.siteId = 1 # Needs to match id entry in Sites database
|
||||||
if autostart:
|
if autostart:
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
|
|
|
@ -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()
|
|
||||||
|
vbox1 = gtk.VBox(True, 0)
|
||||||
|
hbox.pack_start(vbox1, True, True, 0)
|
||||||
|
vbox2 = gtk.VBox(True, 0)
|
||||||
|
hbox.pack_start(vbox2, True, True, 0)
|
||||||
|
|
||||||
self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:")
|
self.intervalLabel = gtk.Label("Time between imports in seconds:")
|
||||||
self.settingsHBox.pack_start(self.intervalLabel)
|
self.intervalLabel.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
self.intervalLabel.show()
|
vbox1.pack_start(self.intervalLabel, True, True, 0)
|
||||||
|
|
||||||
self.intervalEntry=gtk.Entry()
|
hbox = gtk.HBox(False, 0)
|
||||||
|
vbox2.pack_start(hbox, True, True, 0)
|
||||||
|
self.intervalEntry = gtk.Entry()
|
||||||
self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval")))
|
self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval")))
|
||||||
self.settingsHBox.pack_start(self.intervalEntry)
|
hbox.pack_start(self.intervalEntry, False, False, 0)
|
||||||
self.intervalEntry.show()
|
lbl1 = gtk.Label()
|
||||||
|
hbox.pack_start(lbl1, expand=True, fill=True)
|
||||||
|
|
||||||
self.addSites(self.mainVBox)
|
lbl = gtk.Label('')
|
||||||
|
vbox1.pack_start(lbl, expand=True, fill=True)
|
||||||
|
lbl = gtk.Label('')
|
||||||
|
vbox2.pack_start(lbl, expand=True, fill=True)
|
||||||
|
|
||||||
|
self.addSites(vbox1, vbox2)
|
||||||
|
|
||||||
|
hbox = gtk.HBox(False, 0)
|
||||||
|
self.mainVBox.pack_start(hbox, expand=True, padding=3)
|
||||||
|
|
||||||
|
hbox = gtk.HBox(False, 0)
|
||||||
|
self.mainVBox.pack_start(hbox, expand=False, padding=3)
|
||||||
|
|
||||||
|
lbl1 = gtk.Label()
|
||||||
|
hbox.pack_start(lbl1, expand=True, fill=False)
|
||||||
|
|
||||||
self.doAutoImportBool = False
|
self.doAutoImportBool = False
|
||||||
self.startButton=gtk.ToggleButton("Start Autoimport")
|
self.startButton = gtk.ToggleButton(" _Start Autoimport ")
|
||||||
self.startButton.connect("clicked", self.startClicked, "start clicked")
|
self.startButton.connect("clicked", self.startClicked, "start clicked")
|
||||||
self.mainVBox.add(self.startButton)
|
hbox.pack_start(self.startButton, expand=False, fill=False)
|
||||||
self.startButton.show()
|
|
||||||
|
lbl2 = gtk.Label()
|
||||||
|
hbox.pack_start(lbl2, expand=True, fill=False)
|
||||||
|
|
||||||
|
hbox = gtk.HBox(False, 0)
|
||||||
|
hbox.show()
|
||||||
|
self.mainVBox.pack_start(hbox, expand=True, padding=3)
|
||||||
|
self.mainVBox.show_all()
|
||||||
|
|
||||||
|
|
||||||
#end of GuiAutoImport.__init__
|
#end of GuiAutoImport.__init__
|
||||||
|
@ -127,7 +154,7 @@ class GuiAutoImport (threading.Thread):
|
||||||
# to watch.
|
# to watch.
|
||||||
if widget.get_active(): # toggled on
|
if widget.get_active(): # toggled on
|
||||||
self.doAutoImportBool = True
|
self.doAutoImportBool = True
|
||||||
widget.set_label(u'Stop Autoimport')
|
widget.set_label(u' _Stop Autoimport ')
|
||||||
if self.pipe_to_hud is None:
|
if self.pipe_to_hud is None:
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
command = "python HUD_main.py" + " %s" % (self.database)
|
command = "python HUD_main.py" + " %s" % (self.database)
|
||||||
|
@ -163,7 +190,7 @@ class GuiAutoImport (threading.Thread):
|
||||||
#print >>self.pipe_to_hud.stdin, "\n"
|
#print >>self.pipe_to_hud.stdin, "\n"
|
||||||
self.pipe_to_hud.communicate('\n') # waits for process to terminate
|
self.pipe_to_hud.communicate('\n') # waits for process to terminate
|
||||||
self.pipe_to_hud = None
|
self.pipe_to_hud = None
|
||||||
self.startButton.set_label(u'Start Autoimport')
|
self.startButton.set_label(u' _Start Autoimport ')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,40 +204,41 @@ class GuiAutoImport (threading.Thread):
|
||||||
#Create the site line given required info and setup callbacks
|
#Create the site line given required info and setup callbacks
|
||||||
#enabling and disabling sites from this interface not possible
|
#enabling and disabling sites from this interface not possible
|
||||||
#expects a box to layout the line horizontally
|
#expects a box to layout the line horizontally
|
||||||
def createSiteLine(self, hbox, site, iconpath, hhpath, filter_name, active = True):
|
def createSiteLine(self, hbox1, hbox2, site, iconpath, hhpath, filter_name, active = True):
|
||||||
label = gtk.Label(site + " auto-import:")
|
label = gtk.Label(site + " auto-import:")
|
||||||
hbox.pack_start(label, False, False, 0)
|
hbox1.pack_start(label, False, False, 3)
|
||||||
label.show()
|
label.show()
|
||||||
|
|
||||||
dirPath=gtk.Entry()
|
dirPath=gtk.Entry()
|
||||||
dirPath.set_text(hhpath)
|
dirPath.set_text(hhpath)
|
||||||
hbox.pack_start(dirPath, False, True, 0)
|
hbox1.pack_start(dirPath, True, True, 3)
|
||||||
dirPath.show()
|
dirPath.show()
|
||||||
|
|
||||||
browseButton=gtk.Button("Browse...")
|
browseButton=gtk.Button("Browse...")
|
||||||
browseButton.connect("clicked", self.browseClicked, [site] + [dirPath])
|
browseButton.connect("clicked", self.browseClicked, [site] + [dirPath])
|
||||||
hbox.pack_start(browseButton, False, False, 0)
|
hbox2.pack_start(browseButton, False, False, 3)
|
||||||
browseButton.show()
|
browseButton.show()
|
||||||
|
|
||||||
label = gtk.Label(site + " filter:")
|
label = gtk.Label(' ' + site + " filter:")
|
||||||
hbox.pack_start(label, False, False, 0)
|
hbox2.pack_start(label, False, False, 3)
|
||||||
label.show()
|
label.show()
|
||||||
|
|
||||||
filter=gtk.Entry()
|
filter=gtk.Entry()
|
||||||
filter.set_text(filter_name)
|
filter.set_text(filter_name)
|
||||||
hbox.pack_start(filter, False, True, 0)
|
hbox2.pack_start(filter, True, True, 3)
|
||||||
filter.show()
|
filter.show()
|
||||||
|
|
||||||
def addSites(self, vbox):
|
def addSites(self, vbox1, vbox2):
|
||||||
the_sites = self.config.get_supported_sites()
|
the_sites = self.config.get_supported_sites()
|
||||||
for site in the_sites:
|
for site in the_sites:
|
||||||
pathHBox = gtk.HBox(False, 0)
|
pathHBox1 = gtk.HBox(False, 0)
|
||||||
vbox.pack_start(pathHBox, False, True, 0)
|
vbox1.pack_start(pathHBox1, False, True, 0)
|
||||||
pathHBox.show()
|
pathHBox2 = gtk.HBox(False, 0)
|
||||||
|
vbox2.pack_start(pathHBox2, False, True, 0)
|
||||||
|
|
||||||
params = self.config.get_site_parameters(site)
|
params = self.config.get_site_parameters(site)
|
||||||
paths = self.config.get_default_paths(site)
|
paths = self.config.get_default_paths(site)
|
||||||
self.createSiteLine(pathHBox, site, False, paths['hud-defaultPath'], params['converter'], params['enabled'])
|
self.createSiteLine(pathHBox1, pathHBox2, site, False, paths['hud-defaultPath'], params['converter'], params['enabled'])
|
||||||
self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']]
|
self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']]
|
||||||
|
|
||||||
if __name__== "__main__":
|
if __name__== "__main__":
|
||||||
|
|
28
pyfpdb/GuiBulkImport.py
Normal file → Executable file
28
pyfpdb/GuiBulkImport.py
Normal file → Executable file
|
@ -30,7 +30,6 @@ import gtk
|
||||||
# fpdb/FreePokerTools modules
|
# fpdb/FreePokerTools modules
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import fpdb_db
|
|
||||||
import Configuration
|
import Configuration
|
||||||
|
|
||||||
class GuiBulkImport():
|
class GuiBulkImport():
|
||||||
|
@ -66,13 +65,16 @@ class GuiBulkImport():
|
||||||
self.importer.setDropIndexes(cb_model[cb_index][0])
|
self.importer.setDropIndexes(cb_model[cb_index][0])
|
||||||
else:
|
else:
|
||||||
self.importer.setDropIndexes("auto")
|
self.importer.setDropIndexes("auto")
|
||||||
hhc=self.cbfilter.get_model()[self.cbfilter.get_active()][0]
|
sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0]
|
||||||
self.lab_info.set_text("Importing")
|
self.lab_info.set_text("Importing")
|
||||||
|
|
||||||
self.importer.addBulkImportImportFileOrDir(self.inputFile,filter=hhc)
|
self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename)
|
||||||
self.importer.setCallHud(False)
|
self.importer.setCallHud(False)
|
||||||
starttime = time()
|
starttime = time()
|
||||||
(stored, dups, partial, errs, ttime) = self.importer.runImport()
|
(stored, dups, partial, errs, ttime) = self.importer.runImport()
|
||||||
|
ttime = time() - starttime
|
||||||
|
if ttime == 0:
|
||||||
|
ttime = 1
|
||||||
print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\
|
print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\
|
||||||
% (stored, dups, partial, errs, ttime, stored / ttime)
|
% (stored, dups, partial, errs, ttime, stored / ttime)
|
||||||
self.importer.clearFileList()
|
self.importer.clearFileList()
|
||||||
|
@ -83,8 +85,7 @@ class GuiBulkImport():
|
||||||
"""returns the vbox of this thread"""
|
"""returns the vbox of this thread"""
|
||||||
return self.vbox
|
return self.vbox
|
||||||
|
|
||||||
def __init__(self, db, settings, config):
|
def __init__(self, settings, config):
|
||||||
self.db = db # this is an instance of fpdb_db
|
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.config = config
|
self.config = config
|
||||||
self.importer = fpdb_import.Importer(self, self.settings,
|
self.importer = fpdb_import.Importer(self, self.settings,
|
||||||
|
@ -175,11 +176,9 @@ class GuiBulkImport():
|
||||||
|
|
||||||
# ComboBox - filter
|
# ComboBox - filter
|
||||||
self.cbfilter = gtk.combo_box_new_text()
|
self.cbfilter = gtk.combo_box_new_text()
|
||||||
self.cbfilter.append_text("passthrough")
|
for w in self.config.hhcs:
|
||||||
self.cbfilter.append_text("BetfairToFpdb")
|
print w
|
||||||
self.cbfilter.append_text("EverleafToFpdb")
|
self.cbfilter.append_text(w)
|
||||||
self.cbfilter.append_text("FulltiltToFpdb")
|
|
||||||
self.cbfilter.append_text("PokerStarsToFpdb")
|
|
||||||
self.cbfilter.set_active(0)
|
self.cbfilter.set_active(0)
|
||||||
self.table.attach(self.cbfilter, 3, 4, 2, 3, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
|
self.table.attach(self.cbfilter, 3, 4, 2, 3, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
|
||||||
self.cbfilter.show()
|
self.cbfilter.show()
|
||||||
|
@ -220,8 +219,8 @@ def main(argv=None):
|
||||||
help="Input file in quiet mode")
|
help="Input file in quiet mode")
|
||||||
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True,
|
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True,
|
||||||
help="don't start gui; deprecated (just give a filename with -f).")
|
help="don't start gui; deprecated (just give a filename with -f).")
|
||||||
parser.add_option("-c", "--convert", dest="filtername", default="passthrough", metavar="FILTER",
|
parser.add_option("-c", "--convert", dest="filtername", default="PokerStars", metavar="FILTER",
|
||||||
help="Conversion filter (*passthrough, FullTiltToFpdb, PokerStarsToFpdb, EverleafToFpdb)")
|
help="Conversion filter (*Full Tilt Poker, PokerStars, Everleaf)")
|
||||||
parser.add_option("-x", "--failOnError", action="store_true", default=False,
|
parser.add_option("-x", "--failOnError", action="store_true", default=False,
|
||||||
help="If this option is passed it quits when it encounters any error")
|
help="If this option is passed it quits when it encounters any error")
|
||||||
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
|
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
|
||||||
|
@ -229,7 +228,6 @@ def main(argv=None):
|
||||||
(options, sys.argv) = parser.parse_args(args = argv)
|
(options, sys.argv) = parser.parse_args(args = argv)
|
||||||
|
|
||||||
config = Configuration.Config()
|
config = Configuration.Config()
|
||||||
db = None
|
|
||||||
|
|
||||||
settings = {}
|
settings = {}
|
||||||
settings['minPrint'] = options.minPrint
|
settings['minPrint'] = options.minPrint
|
||||||
|
@ -245,7 +243,7 @@ def main(argv=None):
|
||||||
print '-q is deprecated. Just use "-f filename" instead'
|
print '-q is deprecated. Just use "-f filename" instead'
|
||||||
# This is because -q on its own causes an error, so -f is necessary and sufficient for cmd line use
|
# This is because -q on its own causes an error, so -f is necessary and sufficient for cmd line use
|
||||||
if not options.filename:
|
if not options.filename:
|
||||||
i = GuiBulkImport(db, settings, config)
|
i = GuiBulkImport(settings, config)
|
||||||
main_window = gtk.Window()
|
main_window = gtk.Window()
|
||||||
main_window.connect('destroy', destroy)
|
main_window.connect('destroy', destroy)
|
||||||
main_window.add(i.vbox)
|
main_window.add(i.vbox)
|
||||||
|
@ -256,7 +254,7 @@ def main(argv=None):
|
||||||
importer = fpdb_import.Importer(False,settings, config)
|
importer = fpdb_import.Importer(False,settings, config)
|
||||||
importer.setDropIndexes("auto")
|
importer.setDropIndexes("auto")
|
||||||
importer.setFailOnError(options.failOnError)
|
importer.setFailOnError(options.failOnError)
|
||||||
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), filter=options.filtername)
|
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername)
|
||||||
importer.setCallHud(False)
|
importer.setCallHud(False)
|
||||||
importer.runImport()
|
importer.runImport()
|
||||||
importer.clearFileList()
|
importer.clearFileList()
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
self.filters = Filters.Filters(db, settings, config, querylist, display = filters_display)
|
||||||
|
self.filters.registerButton1Name("Refresh Graph")
|
||||||
|
self.filters.registerButton1Callback(self.generateGraph)
|
||||||
|
self.filters.registerButton2Name("Export to File")
|
||||||
|
self.filters.registerButton2Callback(self.exportGraph)
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
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,429 @@ 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" : True,
|
||||||
|
"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")
|
||||||
|
, ("bb100xr", True, "bbxr/100", 1.0, "%4.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()
|
||||||
|
dates = self.filters.getDates()
|
||||||
|
sitenos = []
|
||||||
|
playerids = []
|
||||||
|
|
||||||
|
# Which sites are selected?
|
||||||
|
for site in sites:
|
||||||
|
if sites[site] == True:
|
||||||
|
sitenos.append(siteids[site])
|
||||||
|
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
||||||
|
result = self.db.cursor.fetchall()
|
||||||
|
if len(result) == 1:
|
||||||
|
playerids.append(result[0][0])
|
||||||
|
|
||||||
|
if not sitenos:
|
||||||
|
#Should probably pop up here.
|
||||||
|
print "No sites selected - defaulting to PokerStars"
|
||||||
|
sitenos = [2]
|
||||||
|
if not playerids:
|
||||||
|
print "No player ids found"
|
||||||
|
return
|
||||||
|
if not limits:
|
||||||
|
print "No limits found"
|
||||||
|
return
|
||||||
|
|
||||||
|
self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates)
|
||||||
|
|
||||||
|
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates):
|
||||||
|
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, dates)
|
||||||
|
|
||||||
|
# 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, dates)
|
||||||
|
|
||||||
|
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, dates):
|
||||||
|
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, dates)
|
||||||
|
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, dates):
|
||||||
|
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>", '')
|
||||||
|
|
||||||
|
# Filter on dates
|
||||||
|
query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
|
||||||
|
|
||||||
|
#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()
|
||||||
|
siteids = self.filters.getSiteIds()
|
||||||
|
limits = self.filters.getLimits()
|
||||||
|
seats = self.filters.getSeats()
|
||||||
|
dates = self.filters.getDates()
|
||||||
|
sitenos = []
|
||||||
|
playerids = []
|
||||||
|
|
||||||
|
# Which sites are selected?
|
||||||
|
for site in sites:
|
||||||
|
if sites[site] == True:
|
||||||
|
sitenos.append(siteids[site])
|
||||||
|
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
||||||
|
result = self.db.cursor.fetchall()
|
||||||
|
if len(result) == 1:
|
||||||
|
playerids.append(result[0][0])
|
||||||
|
|
||||||
|
if not sitenos:
|
||||||
|
#Should probably pop up here.
|
||||||
|
print "No sites selected - defaulting to PokerStars"
|
||||||
|
sitenos = [2]
|
||||||
|
if not playerids:
|
||||||
|
print "No player ids found"
|
||||||
|
return
|
||||||
|
if not limits:
|
||||||
|
print "No limits found"
|
||||||
|
return
|
||||||
|
|
||||||
|
self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates)
|
||||||
|
|
||||||
|
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates):
|
||||||
|
self.stats_table = gtk.Table(1, 1, False) # gtk table expands as required
|
||||||
|
self.stats_table.set_col_spacings(4)
|
||||||
|
self.stats_table.show()
|
||||||
|
vbox.add(self.stats_table)
|
||||||
|
|
||||||
|
row = 0
|
||||||
|
col = 0
|
||||||
|
for t in self.posnheads:
|
||||||
|
l = gtk.Label(self.posnheads[col])
|
||||||
|
l.show()
|
||||||
|
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
|
||||||
|
col +=1
|
||||||
|
|
||||||
tmp = self.sql.query['playerStatsByPosition']
|
tmp = self.sql.query['playerStatsByPosition']
|
||||||
|
tmp = self.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
|
||||||
result = self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[self.activesite],))
|
self.cursor.execute(tmp)
|
||||||
result = self.cursor.fetchall()
|
result = self.cursor.fetchall()
|
||||||
if not result == ():
|
|
||||||
pid = result[0][0]
|
|
||||||
pid = result[0][0]
|
|
||||||
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
|
|
||||||
self.cursor.execute(tmp)
|
|
||||||
result = self.cursor.fetchall()
|
|
||||||
cols = 16
|
|
||||||
rows = len(result)+1 # +1 for title row
|
|
||||||
self.stats_table = gtk.Table(rows, cols, False)
|
|
||||||
self.stats_table.set_col_spacings(4)
|
|
||||||
self.stats_table.show()
|
|
||||||
vbox.add(self.stats_table)
|
|
||||||
|
|
||||||
# Create header row
|
rows = len(result)
|
||||||
titles = ("Game", "Position", "#", "VPIP", "PFR", "Saw_F", "SawSD", "WtSDwsF", "W$SD", "FlAFq", "TuAFq", "RvAFq", "PoFAFq", "Net($)", "BB/100", "$/hand", "Variance")
|
colnames = [desc[0].lower() for desc in self.cursor.description]
|
||||||
|
|
||||||
col = 0
|
last_game,last_seats,sqlrow = "","",0
|
||||||
row = 0
|
while sqlrow < rows:
|
||||||
for t in titles:
|
if(row%2 == 0):
|
||||||
l = gtk.Label(titles[col])
|
bgcolor = "white"
|
||||||
l.show()
|
else:
|
||||||
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
|
bgcolor = "lightgrey"
|
||||||
col +=1
|
rowprinted=0
|
||||||
|
avgcol = colnames.index('avgseats')
|
||||||
for row in range(rows-1):
|
for col,colname in enumerate(self.posncols):
|
||||||
if(row%2 == 0):
|
if colname in colnames:
|
||||||
bgcolor = "white"
|
sqlcol = colnames.index(colname)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
eb = gtk.EventBox()
|
||||||
|
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
||||||
|
# print blank row between levels:
|
||||||
|
if result[sqlrow][sqlcol]:
|
||||||
|
if sqlrow == 0:
|
||||||
|
l = gtk.Label(result[sqlrow][sqlcol])
|
||||||
|
rowprinted=1
|
||||||
|
elif result[sqlrow][0] != last_game:
|
||||||
|
l = gtk.Label(' ')
|
||||||
|
elif 'show' in seats and seats['show'] and result[sqlrow][avgcol] != last_seats:
|
||||||
|
l = gtk.Label(' ')
|
||||||
else:
|
else:
|
||||||
bgcolor = "lightgrey"
|
l = gtk.Label(result[sqlrow][sqlcol])
|
||||||
for col in range(cols):
|
rowprinted=1
|
||||||
eb = gtk.EventBox()
|
else:
|
||||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
l = gtk.Label(' ')
|
||||||
if result[row][col]:
|
if col == 0:
|
||||||
l = gtk.Label(result[row][col])
|
l.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
else:
|
elif col == 1:
|
||||||
l = gtk.Label(' ')
|
l.set_alignment(xalign=0.5, yalign=0.5)
|
||||||
if col == 0:
|
else:
|
||||||
l.set_alignment(xalign=0.0, yalign=0.5)
|
l.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
else:
|
eb.add(l)
|
||||||
l.set_alignment(xalign=1.0, yalign=0.5)
|
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
||||||
eb.add(l)
|
l.show()
|
||||||
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
eb.show()
|
||||||
l.show()
|
last_game = result[sqlrow][0]
|
||||||
eb.show()
|
last_seats = result[sqlrow][avgcol]
|
||||||
self.fdb.db.commit()
|
if rowprinted:
|
||||||
|
sqlrow = sqlrow+1
|
||||||
|
row = row + 1
|
||||||
|
|
||||||
|
# show totals at bottom
|
||||||
|
tmp = self.sql.query['playerStats']
|
||||||
|
tmp = self.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
|
||||||
|
self.cursor.execute(tmp)
|
||||||
|
result = self.cursor.fetchall()
|
||||||
|
rows = len(result)
|
||||||
|
colnames = [desc[0].lower() for desc in self.cursor.description]
|
||||||
|
|
||||||
|
# blank row between main stats and totals:
|
||||||
|
col = 0
|
||||||
|
if(row%2 == 0):
|
||||||
|
bgcolor = "white"
|
||||||
|
else:
|
||||||
|
bgcolor = "lightgrey"
|
||||||
|
eb = gtk.EventBox()
|
||||||
|
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
||||||
|
l = gtk.Label(' ')
|
||||||
|
eb.add(l)
|
||||||
|
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
||||||
|
l.show()
|
||||||
|
eb.show()
|
||||||
|
row = row + 1
|
||||||
|
|
||||||
|
for sqlrow in range(rows):
|
||||||
|
if(row%2 == 0):
|
||||||
|
bgcolor = "white"
|
||||||
|
else:
|
||||||
|
bgcolor = "lightgrey"
|
||||||
|
for col,colname in enumerate(self.posncols):
|
||||||
|
if colname in colnames:
|
||||||
|
sqlcol = colnames.index(colname)
|
||||||
|
elif colname != "plposition":
|
||||||
|
continue
|
||||||
|
eb = gtk.EventBox()
|
||||||
|
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
||||||
|
if colname == 'plposition':
|
||||||
|
l = gtk.Label('Totals')
|
||||||
|
elif result[sqlrow][sqlcol]:
|
||||||
|
l = gtk.Label(result[sqlrow][sqlcol])
|
||||||
|
else:
|
||||||
|
l = gtk.Label(' ')
|
||||||
|
if col == 0:
|
||||||
|
l.set_alignment(xalign=0.0, yalign=0.5)
|
||||||
|
elif col == 1:
|
||||||
|
l.set_alignment(xalign=0.5, yalign=0.5)
|
||||||
|
else:
|
||||||
|
l.set_alignment(xalign=1.0, yalign=0.5)
|
||||||
|
eb.add(l)
|
||||||
|
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
||||||
|
l.show()
|
||||||
|
eb.show()
|
||||||
|
row = row + 1
|
||||||
|
|
||||||
|
self.db.db.rollback()
|
||||||
#end def fillStatsFrame(self, vbox):
|
#end def fillStatsFrame(self, vbox):
|
||||||
|
|
||||||
def fillPlayerFrame(self, vbox):
|
def refineQuery(self, query, playerids, sitenos, limits, seats, dates):
|
||||||
for site in self.conf.supported_sites.keys():
|
if playerids:
|
||||||
hbox = gtk.HBox(False, 0)
|
nametest = str(tuple(playerids))
|
||||||
vbox.pack_start(hbox, False, True, 0)
|
nametest = nametest.replace("L", "")
|
||||||
hbox.show()
|
nametest = nametest.replace(",)",")")
|
||||||
|
query = query.replace("<player_test>", nametest)
|
||||||
player = self.conf.supported_sites[site].screen_name
|
|
||||||
self.createPlayerLine(hbox, site, player)
|
|
||||||
hbox = gtk.HBox(False, 0)
|
|
||||||
button = gtk.Button("Refresh")
|
|
||||||
button.connect("clicked", self.refreshStats, False)
|
|
||||||
button.show()
|
|
||||||
hbox.add(button)
|
|
||||||
vbox.pack_start(hbox, False, True, 0)
|
|
||||||
hbox.show()
|
|
||||||
|
|
||||||
def fillCardsFrame(self, vbox):
|
|
||||||
hbox1 = gtk.HBox(True,0)
|
|
||||||
hbox1.show()
|
|
||||||
vbox.pack_start(hbox1, True, True, 0)
|
|
||||||
|
|
||||||
cards = [ "A", "K","Q","J","T","9","8","7","6","5","4","3","2" ]
|
|
||||||
|
|
||||||
for j in range(0, len(cards)):
|
|
||||||
hbox1 = gtk.HBox(True,0)
|
|
||||||
hbox1.show()
|
|
||||||
vbox.pack_start(hbox1, True, True, 0)
|
|
||||||
for i in range(0, len(cards)):
|
|
||||||
if i < (j + 1):
|
|
||||||
suit = "o"
|
|
||||||
else:
|
|
||||||
suit = "s"
|
|
||||||
button = gtk.ToggleButton("%s%s%s" %(cards[i], cards[j], suit))
|
|
||||||
button.connect("toggled", self.cardCallback, "%s%s%s" %(cards[i], cards[j], suit))
|
|
||||||
hbox1.pack_start(button, True, True, 0)
|
|
||||||
button.show()
|
|
||||||
|
|
||||||
def createPlayerLine(self, hbox, site, player):
|
|
||||||
if(self.buttongroup == None):
|
|
||||||
button = gtk.RadioButton(None, site + " id:")
|
|
||||||
button.set_active(True)
|
|
||||||
self.buttongroup = button
|
|
||||||
self.activesite = site
|
|
||||||
else:
|
else:
|
||||||
button = gtk.RadioButton(self.buttongroup, site + " id:")
|
query = query.replace("<player_test>", "1 = 2")
|
||||||
hbox.pack_start(button, True, True, 0)
|
|
||||||
button.connect("toggled", self.toggleCallback, site)
|
|
||||||
button.show()
|
|
||||||
|
|
||||||
pname = gtk.Entry()
|
if seats:
|
||||||
pname.set_text(player)
|
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
|
||||||
pname.set_width_chars(20)
|
if 'show' in seats and seats['show']:
|
||||||
hbox.pack_start(pname, False, True, 0)
|
query = query.replace('<groupbyseats>', ',hc.activeSeats')
|
||||||
pname.connect("changed", self.__set_hero_name, site)
|
query = query.replace('<orderbyseats>', ',stats.AvgSeats')
|
||||||
#TODO: Look at GtkCompletion - to fill out usernames
|
else:
|
||||||
pname.show()
|
query = query.replace('<groupbyseats>', '')
|
||||||
self.__set_hero_name(pname, site)
|
query = query.replace('<orderbyseats>', '')
|
||||||
|
else:
|
||||||
|
query = query.replace('<seats_test>', 'between 0 and 100')
|
||||||
|
query = query.replace('<groupbyseats>', '')
|
||||||
|
query = query.replace('<orderbyseats>', '')
|
||||||
|
|
||||||
def __set_hero_name(self, w, site):
|
if [x for x in limits if str(x).isdigit()]:
|
||||||
self.heroes[site] = w.get_text()
|
blindtest = str(tuple([x for x in limits if str(x).isdigit()]))
|
||||||
|
blindtest = blindtest.replace("L", "")
|
||||||
def __init__(self, db, config, querylist, debug=True):
|
blindtest = blindtest.replace(",)",")")
|
||||||
self.debug=debug
|
query = query.replace("<gtbigBlind_test>", "gt.bigBlind in " + blindtest)
|
||||||
self.conf=config
|
else:
|
||||||
|
query = query.replace("<gtbigBlind_test>", "gt.bigBlind = -1 ")
|
||||||
# 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
|
groupLevels = "show" not in str(limits)
|
||||||
|
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")
|
||||||
|
|
||||||
self.activesite = None
|
# Filter on dates
|
||||||
self.buttongroup = None
|
query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@
|
||||||
screen_name="YOUR SCREEN NAME HERE"
|
screen_name="YOUR SCREEN NAME HERE"
|
||||||
site_path="C:/Program Files/PokerStars/"
|
site_path="C:/Program Files/PokerStars/"
|
||||||
HH_path="C:/Program Files/PokerStars/HandHistory/YOUR SCREEN NAME HERE/"
|
HH_path="C:/Program Files/PokerStars/HandHistory/YOUR SCREEN NAME HERE/"
|
||||||
decoder="pokerstars_decode_table"
|
decoder="pokerstars_decode_table"
|
||||||
|
converter="PokerStarsToFpdb"
|
||||||
bgcolor="#000000"
|
bgcolor="#000000"
|
||||||
fgcolor="#FFFFFF"
|
fgcolor="#FFFFFF"
|
||||||
hudopacity="1.0"
|
hudopacity="1.0"
|
||||||
font="Sans"
|
font="Sans"
|
||||||
font_size="8"
|
font_size="8"
|
||||||
converter="passthrough"
|
|
||||||
supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
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>
|
||||||
|
@ -74,12 +74,12 @@
|
||||||
site_path="C:/Program Files/Full Tilt Poker/"
|
site_path="C:/Program Files/Full Tilt Poker/"
|
||||||
HH_path="C:/Program Files/Full Tilt Poker/HandHistory/YOUR SCREEN NAME HERE/"
|
HH_path="C:/Program Files/Full Tilt Poker/HandHistory/YOUR SCREEN NAME HERE/"
|
||||||
decoder="fulltilt_decode_table"
|
decoder="fulltilt_decode_table"
|
||||||
|
converter="FulltiltToFpdb"
|
||||||
bgcolor="#000000"
|
bgcolor="#000000"
|
||||||
fgcolor="#FFFFFF"
|
fgcolor="#FFFFFF"
|
||||||
hudopacity="1.0"
|
hudopacity="1.0"
|
||||||
font="Sans"
|
font="Sans"
|
||||||
font_size="8"
|
font_size="8"
|
||||||
converter="passthrough"
|
|
||||||
supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
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>
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
site_path=""
|
site_path=""
|
||||||
HH_path=""
|
HH_path=""
|
||||||
decoder="everleaf_decode_table"
|
decoder="everleaf_decode_table"
|
||||||
converter="EverleafToFpdb"
|
converter="EverleafToFpdb"
|
||||||
supported_games="holdem">
|
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>
|
||||||
|
@ -288,6 +288,12 @@
|
||||||
</aw>
|
</aw>
|
||||||
</aux_windows>
|
</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>
|
||||||
|
|
|
@ -81,10 +81,11 @@ class HUD_main(object):
|
||||||
|
|
||||||
def kill_hud(self, event, table):
|
def kill_hud(self, event, table):
|
||||||
# called by an event in the HUD, to kill this specific HUD
|
# called by an event in the HUD, to kill this specific HUD
|
||||||
self.hud_dict[table].kill()
|
if table in self.hud_dict:
|
||||||
self.hud_dict[table].main_window.destroy()
|
self.hud_dict[table].kill()
|
||||||
self.vb.remove(self.hud_dict[table].tablehudlabel)
|
self.hud_dict[table].main_window.destroy()
|
||||||
del(self.hud_dict[table])
|
self.vb.remove(self.hud_dict[table].tablehudlabel)
|
||||||
|
del(self.hud_dict[table])
|
||||||
self.main_window.resize(1,1)
|
self.main_window.resize(1,1)
|
||||||
|
|
||||||
def create_HUD(self, new_hand_id, table, table_name, max, poker_game, stat_dict, cards):
|
def create_HUD(self, new_hand_id, table, table_name, max, poker_game, stat_dict, cards):
|
||||||
|
@ -194,6 +195,7 @@ class HUD_main(object):
|
||||||
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, 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")
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Hand:
|
||||||
self.sitename = sitename
|
self.sitename = sitename
|
||||||
self.stats = DerivedStats.DerivedStats(self)
|
self.stats = DerivedStats.DerivedStats(self)
|
||||||
self.gametype = gametype
|
self.gametype = gametype
|
||||||
|
self.starttime = 0
|
||||||
self.handText = handText
|
self.handText = handText
|
||||||
self.handid = 0
|
self.handid = 0
|
||||||
self.tablename = "Slartibartfast"
|
self.tablename = "Slartibartfast"
|
||||||
|
@ -86,10 +87,59 @@ Should not commit, and do minimal selects. Callers may want to cache commits
|
||||||
db: a connected fpdb_db object"""
|
db: a connected fpdb_db object"""
|
||||||
# TODO:
|
# TODO:
|
||||||
# Players - base playerid and siteid tuple
|
# Players - base playerid and siteid tuple
|
||||||
|
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
|
||||||
# HudCache data to come from DerivedStats class
|
# HudCache data to come from DerivedStats class
|
||||||
# HandsActions - all actions for all players for all streets - self.actions
|
# HandsActions - all actions for all players for all streets - self.actions
|
||||||
# BoardCards - ?
|
# BoardCards - Skip - no longer necessary?
|
||||||
# Hands - Summary information of hand indexed by handId - gameinfo
|
# Hands - Summary information of hand indexed by handId - gameinfo
|
||||||
|
hh['siteHandNo'] = self.handid
|
||||||
|
# gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||||
|
#
|
||||||
|
hh['handStart'] = self.starttime
|
||||||
|
# seats TINYINT NOT NULL,
|
||||||
|
#
|
||||||
|
hh['tableName'] = self.tablename
|
||||||
|
hh['maxSeats'] = self.maxseats
|
||||||
|
# boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */
|
||||||
|
# boardcard2 smallint,
|
||||||
|
# boardcard3 smallint,
|
||||||
|
# boardcard4 smallint,
|
||||||
|
# boardcard5 smallint,
|
||||||
|
# texture smallint,
|
||||||
|
# playersVpi SMALLINT NOT NULL, /* num of players vpi */
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet2 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet3 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet4 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtShowdown SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# street0Raises TINYINT NOT NULL, /* num small bets paid to see flop/street4, including blind */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street1Raises TINYINT NOT NULL, /* num small bets paid to see turn/street5 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street2Raises TINYINT NOT NULL, /* num big bets paid to see river/street6 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street3Raises TINYINT NOT NULL, /* num big bets paid to see sd/street7 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street4Raises TINYINT NOT NULL, /* num big bets paid to see showdown */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street1Pot INT, /* pot size at flop/street4 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street2Pot INT, /* pot size at turn/street5 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street3Pot INT, /* pot size at river/street6 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street4Pot INT, /* pot size at sd/street7 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# showdownPot INT, /* pot size at sd/street7 */
|
||||||
|
# comment TEXT,
|
||||||
|
# commentTs DATETIME
|
||||||
|
handid = db.storeHand(hh)
|
||||||
# HandsPlayers - ? ... Do we fix winnings?
|
# HandsPlayers - ? ... Do we fix winnings?
|
||||||
# Tourneys ?
|
# Tourneys ?
|
||||||
# TourneysPlayers
|
# TourneysPlayers
|
||||||
|
|
|
@ -112,7 +112,9 @@ class HandHistoryConverter():
|
||||||
def start(self):
|
def start(self):
|
||||||
"""process a hand at a time from the input specified by in_path.
|
"""process a hand at a time from the input specified by in_path.
|
||||||
If in follow mode, wait for more data to turn up.
|
If in follow mode, wait for more data to turn up.
|
||||||
Otherwise, finish at eof..."""
|
Otherwise, finish at eof...
|
||||||
|
|
||||||
|
"""
|
||||||
starttime = time.time()
|
starttime = time.time()
|
||||||
if not self.sanityCheck():
|
if not self.sanityCheck():
|
||||||
print "Cowardly refusing to continue after failed sanity check"
|
print "Cowardly refusing to continue after failed sanity check"
|
||||||
|
@ -137,7 +139,11 @@ Otherwise, finish at eof..."""
|
||||||
|
|
||||||
def tailHands(self):
|
def tailHands(self):
|
||||||
"""Generator of handTexts from a tailed file:
|
"""Generator of handTexts from a tailed file:
|
||||||
Tail the in_path file and yield handTexts separated by re_SplitHands"""
|
Tail the in_path file and yield handTexts separated by re_SplitHands.
|
||||||
|
This requires a regex that greedily groups and matches the 'splitter' between hands,
|
||||||
|
which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
|
|
||||||
|
"""
|
||||||
if self.in_path == '-': raise StopIteration
|
if self.in_path == '-': raise StopIteration
|
||||||
interval = 1.0 # seconds to sleep between reads for new data
|
interval = 1.0 # seconds to sleep between reads for new data
|
||||||
fd = codecs.open(self.in_path,'r', self.codepage)
|
fd = codecs.open(self.in_path,'r', self.codepage)
|
||||||
|
@ -161,7 +167,7 @@ Tail the in_path file and yield handTexts separated by re_SplitHands"""
|
||||||
else:
|
else:
|
||||||
# yield hands
|
# yield hands
|
||||||
data = data + newdata
|
data = data + newdata
|
||||||
result = self.re_SplitHands.split(data)
|
result = self.re_TailSplitHands.split(data)
|
||||||
result = iter(result)
|
result = iter(result)
|
||||||
data = ''
|
data = ''
|
||||||
# --x data (- is bit of splitter, x is paragraph) yield,...,keep
|
# --x data (- is bit of splitter, x is paragraph) yield,...,keep
|
||||||
|
|
|
@ -72,6 +72,7 @@ class OnGame(HandHistoryConverter):
|
||||||
HandHistoryConverter.__init__(self, config, file, sitename="OnGame") # Call super class init.
|
HandHistoryConverter.__init__(self, config, file, sitename="OnGame") # Call super class init.
|
||||||
self.sitename = "OnGame"
|
self.sitename = "OnGame"
|
||||||
self.setFileType("text", "cp1252")
|
self.setFileType("text", "cp1252")
|
||||||
|
self.siteId = 5 # Needs to match id entry in Sites database
|
||||||
#self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
|
#self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
|
||||||
self.rexx.setSplitHandRegex('\n\n\n+')
|
self.rexx.setSplitHandRegex('\n\n\n+')
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,8 @@ class PokerStars(HandHistoryConverter):
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
re_GameInfo = re.compile("PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud|Omaha|Omaha Hi/Lo|Badugi) (?P<LIMIT>No Limit|Limit|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)", re.MULTILINE)
|
re_GameInfo = re.compile("PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud|Omaha|Omaha Hi/Lo|Badugi) (?P<LIMIT>No Limit|Limit|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)", re.MULTILINE)
|
||||||
re_SplitHands = re.compile('(\n\n+)')
|
re_SplitHands = re.compile('\n\n+')
|
||||||
|
re_TailSplitHands = re.compile('(\n\n\n+)')
|
||||||
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
|
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
|
||||||
re_Button = re.compile('Seat #(?P<BUTTON>\d+) is the button', re.MULTILINE)
|
re_Button = re.compile('Seat #(?P<BUTTON>\d+) is the button', re.MULTILINE)
|
||||||
re_PlayerInfo = re.compile('^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[.0-9]+) in chips\)', re.MULTILINE)
|
re_PlayerInfo = re.compile('^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[.0-9]+) in chips\)', re.MULTILINE)
|
||||||
|
@ -43,6 +44,7 @@ follow : whether to tail -f the input"""
|
||||||
logging.info("Initialising PokerStars converter class")
|
logging.info("Initialising PokerStars converter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
self.siteId = 2 # Needs to match id entry in Sites database
|
||||||
if autostart:
|
if autostart:
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
|
@ -72,7 +74,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"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
491
pyfpdb/SQL.py
491
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,148 +172,328 @@ 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, Players.name, seatNo
|
AND hc.styleKey > %s
|
||||||
|
/* styleKey is currently 'd' (for date) followed by a yyyymmdd
|
||||||
|
date key. Set it to 0000000 or similar to get all records */
|
||||||
|
/* also check activeseats here? even if only 3 groups eg 2-3/4-6/7+ ??
|
||||||
|
e.g. could use a multiplier:
|
||||||
|
AND h.seats > X / 1.25 and hp.seats < X * 1.25
|
||||||
|
where X is the number of active players at the current table (and
|
||||||
|
1.25 would be a config value so user could change it)
|
||||||
|
*/
|
||||||
|
GROUP BY hc.PlayerId, hp.seatNo, p.name
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# same as above except stats are aggregated for all blind/limit levels
|
# same as above except stats are aggregated for all blind/limit levels
|
||||||
self.query['get_stats_from_hand_aggregated'] = """
|
self.query['get_stats_from_hand_aggregated'] = """
|
||||||
SELECT HudCache.playerId AS player_id,
|
SELECT hc.playerId AS player_id,
|
||||||
sum(HDs) AS n,
|
max(case when hc.gametypeId = h.gametypeId
|
||||||
sum(street0VPI) AS vpip,
|
then hp.seatNo
|
||||||
sum(street0Aggr) AS pfr,
|
else -1
|
||||||
sum(street0_3B4BChance) AS TB_opp_0,
|
end) AS seat,
|
||||||
sum(street0_3B4BDone) AS TB_0,
|
p.name AS screen_name,
|
||||||
sum(street1Seen) AS saw_f,
|
sum(hc.HDs) AS n,
|
||||||
sum(street1Seen) AS saw_1,
|
sum(hc.street0VPI) AS vpip,
|
||||||
sum(street2Seen) AS saw_2,
|
sum(hc.street0Aggr) AS pfr,
|
||||||
sum(street3Seen) AS saw_3,
|
sum(hc.street0_3BChance) AS TB_opp_0,
|
||||||
sum(street4Seen) AS saw_4,
|
sum(hc.street0_3BDone) AS TB_0,
|
||||||
sum(sawShowdown) AS sd,
|
sum(hc.street1Seen) AS saw_f,
|
||||||
sum(street1Aggr) AS aggr_1,
|
sum(hc.street1Seen) AS saw_1,
|
||||||
sum(street2Aggr) AS aggr_2,
|
sum(hc.street2Seen) AS saw_2,
|
||||||
sum(street3Aggr) AS aggr_3,
|
sum(hc.street3Seen) AS saw_3,
|
||||||
sum(street4Aggr) AS aggr_4,
|
sum(hc.street4Seen) AS saw_4,
|
||||||
sum(otherRaisedStreet1) AS was_raised_1,
|
sum(hc.sawShowdown) AS sd,
|
||||||
sum(otherRaisedStreet2) AS was_raised_2,
|
sum(hc.street1Aggr) AS aggr_1,
|
||||||
sum(otherRaisedStreet3) AS was_raised_3,
|
sum(hc.street2Aggr) AS aggr_2,
|
||||||
sum(otherRaisedStreet4) AS was_raised_4,
|
sum(hc.street3Aggr) AS aggr_3,
|
||||||
sum(foldToOtherRaisedStreet1) AS f_freq_1,
|
sum(hc.street4Aggr) AS aggr_4,
|
||||||
sum(foldToOtherRaisedStreet2) AS f_freq_2,
|
sum(hc.otherRaisedStreet1) AS was_raised_1,
|
||||||
sum(foldToOtherRaisedStreet3) AS f_freq_3,
|
sum(hc.otherRaisedStreet2) AS was_raised_2,
|
||||||
sum(foldToOtherRaisedStreet4) AS f_freq_4,
|
sum(hc.otherRaisedStreet3) AS was_raised_3,
|
||||||
sum(wonWhenSeenStreet1) AS w_w_s_1,
|
sum(hc.otherRaisedStreet4) AS was_raised_4,
|
||||||
sum(wonAtSD) AS wmsd,
|
sum(hc.foldToOtherRaisedStreet1) AS f_freq_1,
|
||||||
sum(stealAttemptChance) AS steal_opp,
|
sum(hc.foldToOtherRaisedStreet2) AS f_freq_2,
|
||||||
sum(stealAttempted) AS steal,
|
sum(hc.foldToOtherRaisedStreet3) AS f_freq_3,
|
||||||
sum(foldSbToStealChance) AS SBstolen,
|
sum(hc.foldToOtherRaisedStreet4) AS f_freq_4,
|
||||||
sum(foldedSbToSteal) AS SBnotDef,
|
sum(hc.wonWhenSeenStreet1) AS w_w_s_1,
|
||||||
sum(foldBbToStealChance) AS BBstolen,
|
sum(hc.wonAtSD) AS wmsd,
|
||||||
sum(foldedBbToSteal) AS BBnotDef,
|
sum(hc.stealAttemptChance) AS steal_opp,
|
||||||
sum(street1CBChance) AS CB_opp_1,
|
sum(hc.stealAttempted) AS steal,
|
||||||
sum(street1CBDone) AS CB_1,
|
sum(hc.foldSbToStealChance) AS SBstolen,
|
||||||
sum(street2CBChance) AS CB_opp_2,
|
sum(hc.foldedSbToSteal) AS SBnotDef,
|
||||||
sum(street2CBDone) AS CB_2,
|
sum(hc.foldBbToStealChance) AS BBstolen,
|
||||||
sum(street3CBChance) AS CB_opp_3,
|
sum(hc.foldedBbToSteal) AS BBnotDef,
|
||||||
sum(street3CBDone) AS CB_3,
|
sum(hc.street1CBChance) AS CB_opp_1,
|
||||||
sum(street4CBChance) AS CB_opp_4,
|
sum(hc.street1CBDone) AS CB_1,
|
||||||
sum(street4CBDone) AS CB_4,
|
sum(hc.street2CBChance) AS CB_opp_2,
|
||||||
sum(foldToStreet1CBChance) AS f_cb_opp_1,
|
sum(hc.street2CBDone) AS CB_2,
|
||||||
sum(foldToStreet1CBDone) AS f_cb_1,
|
sum(hc.street3CBChance) AS CB_opp_3,
|
||||||
sum(foldToStreet2CBChance) AS f_cb_opp_2,
|
sum(hc.street3CBDone) AS CB_3,
|
||||||
sum(foldToStreet2CBDone) AS f_cb_2,
|
sum(hc.street4CBChance) AS CB_opp_4,
|
||||||
sum(foldToStreet3CBChance) AS f_cb_opp_3,
|
sum(hc.street4CBDone) AS CB_4,
|
||||||
sum(foldToStreet3CBDone) AS f_cb_3,
|
sum(hc.foldToStreet1CBChance) AS f_cb_opp_1,
|
||||||
sum(foldToStreet4CBChance) AS f_cb_opp_4,
|
sum(hc.foldToStreet1CBDone) AS f_cb_1,
|
||||||
sum(foldToStreet4CBDone) AS f_cb_4,
|
sum(hc.foldToStreet2CBChance) AS f_cb_opp_2,
|
||||||
sum(totalProfit) AS net,
|
sum(hc.foldToStreet2CBDone) AS f_cb_2,
|
||||||
sum(street1CheckCallRaiseChance) AS ccr_opp_1,
|
sum(hc.foldToStreet3CBChance) AS f_cb_opp_3,
|
||||||
sum(street1CheckCallRaiseDone) AS ccr_1,
|
sum(hc.foldToStreet3CBDone) AS f_cb_3,
|
||||||
sum(street2CheckCallRaiseChance) AS ccr_opp_2,
|
sum(hc.foldToStreet4CBChance) AS f_cb_opp_4,
|
||||||
sum(street2CheckCallRaiseDone) AS ccr_2,
|
sum(hc.foldToStreet4CBDone) AS f_cb_4,
|
||||||
sum(street3CheckCallRaiseChance) AS ccr_opp_3,
|
sum(hc.totalProfit) AS net,
|
||||||
sum(street3CheckCallRaiseDone) AS ccr_3,
|
sum(hc.street1CheckCallRaiseChance) AS ccr_opp_1,
|
||||||
sum(street4CheckCallRaiseChance) AS ccr_opp_4,
|
sum(hc.street1CheckCallRaiseDone) AS ccr_1,
|
||||||
sum(street4CheckCallRaiseDone) AS ccr_4
|
sum(hc.street2CheckCallRaiseChance) AS ccr_opp_2,
|
||||||
FROM HudCache, Hands
|
sum(hc.street2CheckCallRaiseDone) AS ccr_2,
|
||||||
WHERE HudCache.PlayerId in
|
sum(hc.street3CheckCallRaiseChance) AS ccr_opp_3,
|
||||||
(SELECT PlayerId FROM HandsPlayers
|
sum(hc.street3CheckCallRaiseDone) AS ccr_3,
|
||||||
WHERE handId = %s)
|
sum(hc.street4CheckCallRaiseChance) AS ccr_opp_4,
|
||||||
AND Hands.id = %s
|
sum(hc.street4CheckCallRaiseDone) AS ccr_4
|
||||||
AND HudCache.gametypeId in
|
FROM Hands h
|
||||||
(SELECT gt1.id from Gametypes gt1, Gametypes gt2, Hands
|
INNER JOIN HandsPlayers hp ON (hp.handId = %s)
|
||||||
WHERE gt1.siteid = gt2.siteid
|
INNER JOIN HudCache hc ON (hc.playerId = hp.playerId)
|
||||||
AND gt1.type = gt2.type
|
INNER JOIN Players p ON (p.id = hc.playerId)
|
||||||
AND gt1.category = gt2.category
|
WHERE h.id = %s
|
||||||
AND gt1.limittype = gt2.limittype
|
AND hc.styleKey > %s
|
||||||
AND gt2.id = Hands.gametypeId
|
/* styleKey is currently 'd' (for date) followed by a yyyymmdd
|
||||||
AND Hands.id = %s)
|
date key. Set it to 0000000 or similar to get all records */
|
||||||
GROUP BY HudCache.PlayerId, Players.name, seatNo
|
/* also check activeseats here? even if only 3 groups eg 2-3/4-6/7+ ??
|
||||||
|
e.g. could use a multiplier:
|
||||||
|
AND h.seats > %s / 1.25 and hp.seats < %s * 1.25
|
||||||
|
where %s is the number of active players at the current table (and
|
||||||
|
1.25 would be a config value so user could change it)
|
||||||
|
*/
|
||||||
|
AND hc.gametypeId+0 in
|
||||||
|
(SELECT gt1.id from Gametypes gt1, Gametypes gt2
|
||||||
|
WHERE gt1.siteid = gt2.siteid
|
||||||
|
AND gt1.type = gt2.type
|
||||||
|
AND gt1.category = gt2.category
|
||||||
|
AND gt1.limittype = gt2.limittype
|
||||||
|
AND gt2.id = h.gametypeId)
|
||||||
|
GROUP BY hc.PlayerId, p.name, hc.styleKey
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if db_server == 'mysql':
|
||||||
|
self.query['get_stats_from_hand_session'] = """
|
||||||
|
SELECT hp.playerId AS player_id,
|
||||||
|
hp.handId AS hand_id,
|
||||||
|
hp.seatNo AS seat,
|
||||||
|
p.name AS screen_name,
|
||||||
|
h.seats AS seats,
|
||||||
|
1 AS n,
|
||||||
|
cast(hp2.street0VPI as <signed>integer) AS vpip,
|
||||||
|
cast(hp2.street0Aggr as <signed>integer) AS pfr,
|
||||||
|
cast(hp2.street0_3BChance as <signed>integer) AS TB_opp_0,
|
||||||
|
cast(hp2.street0_3BDone as <signed>integer) AS TB_0,
|
||||||
|
cast(hp2.street1Seen as <signed>integer) AS saw_f,
|
||||||
|
cast(hp2.street1Seen as <signed>integer) AS saw_1,
|
||||||
|
cast(hp2.street2Seen as <signed>integer) AS saw_2,
|
||||||
|
cast(hp2.street3Seen as <signed>integer) AS saw_3,
|
||||||
|
cast(hp2.street4Seen as <signed>integer) AS saw_4,
|
||||||
|
cast(hp2.sawShowdown as <signed>integer) AS sd,
|
||||||
|
cast(hp2.street1Aggr as <signed>integer) AS aggr_1,
|
||||||
|
cast(hp2.street2Aggr as <signed>integer) AS aggr_2,
|
||||||
|
cast(hp2.street3Aggr as <signed>integer) AS aggr_3,
|
||||||
|
cast(hp2.street4Aggr as <signed>integer) AS aggr_4,
|
||||||
|
cast(hp2.otherRaisedStreet1 as <signed>integer) AS was_raised_1,
|
||||||
|
cast(hp2.otherRaisedStreet2 as <signed>integer) AS was_raised_2,
|
||||||
|
cast(hp2.otherRaisedStreet3 as <signed>integer) AS was_raised_3,
|
||||||
|
cast(hp2.otherRaisedStreet4 as <signed>integer) AS was_raised_4,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet1 as <signed>integer) AS f_freq_1,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet2 as <signed>integer) AS f_freq_2,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet3 as <signed>integer) AS f_freq_3,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet4 as <signed>integer) AS f_freq_4,
|
||||||
|
cast(hp2.wonWhenSeenStreet1 as <signed>integer) AS w_w_s_1,
|
||||||
|
cast(hp2.wonAtSD as <signed>integer) AS wmsd,
|
||||||
|
cast(hp2.stealAttemptChance as <signed>integer) AS steal_opp,
|
||||||
|
cast(hp2.stealAttempted as <signed>integer) AS steal,
|
||||||
|
cast(hp2.foldSbToStealChance as <signed>integer) AS SBstolen,
|
||||||
|
cast(hp2.foldedSbToSteal as <signed>integer) AS SBnotDef,
|
||||||
|
cast(hp2.foldBbToStealChance as <signed>integer) AS BBstolen,
|
||||||
|
cast(hp2.foldedBbToSteal as <signed>integer) AS BBnotDef,
|
||||||
|
cast(hp2.street1CBChance as <signed>integer) AS CB_opp_1,
|
||||||
|
cast(hp2.street1CBDone as <signed>integer) AS CB_1,
|
||||||
|
cast(hp2.street2CBChance as <signed>integer) AS CB_opp_2,
|
||||||
|
cast(hp2.street2CBDone as <signed>integer) AS CB_2,
|
||||||
|
cast(hp2.street3CBChance as <signed>integer) AS CB_opp_3,
|
||||||
|
cast(hp2.street3CBDone as <signed>integer) AS CB_3,
|
||||||
|
cast(hp2.street4CBChance as <signed>integer) AS CB_opp_4,
|
||||||
|
cast(hp2.street4CBDone as <signed>integer) AS CB_4,
|
||||||
|
cast(hp2.foldToStreet1CBChance as <signed>integer) AS f_cb_opp_1,
|
||||||
|
cast(hp2.foldToStreet1CBDone as <signed>integer) AS f_cb_1,
|
||||||
|
cast(hp2.foldToStreet2CBChance as <signed>integer) AS f_cb_opp_2,
|
||||||
|
cast(hp2.foldToStreet2CBDone as <signed>integer) AS f_cb_2,
|
||||||
|
cast(hp2.foldToStreet3CBChance as <signed>integer) AS f_cb_opp_3,
|
||||||
|
cast(hp2.foldToStreet3CBDone as <signed>integer) AS f_cb_3,
|
||||||
|
cast(hp2.foldToStreet4CBChance as <signed>integer) AS f_cb_opp_4,
|
||||||
|
cast(hp2.foldToStreet4CBDone as <signed>integer) AS f_cb_4,
|
||||||
|
cast(hp2.totalProfit as <signed>integer) AS net,
|
||||||
|
cast(hp2.street1CheckCallRaiseChance as <signed>integer) AS ccr_opp_1,
|
||||||
|
cast(hp2.street1CheckCallRaiseDone as <signed>integer) AS ccr_1,
|
||||||
|
cast(hp2.street2CheckCallRaiseChance as <signed>integer) AS ccr_opp_2,
|
||||||
|
cast(hp2.street2CheckCallRaiseDone as <signed>integer) AS ccr_2,
|
||||||
|
cast(hp2.street3CheckCallRaiseChance as <signed>integer) AS ccr_opp_3,
|
||||||
|
cast(hp2.street3CheckCallRaiseDone as <signed>integer) AS ccr_3,
|
||||||
|
cast(hp2.street4CheckCallRaiseChance as <signed>integer) AS ccr_opp_4,
|
||||||
|
cast(hp2.street4CheckCallRaiseDone as <signed>integer) AS ccr_4
|
||||||
|
FROM
|
||||||
|
Hands h /* players in this hand */
|
||||||
|
INNER JOIN Hands h2 ON (h2.id > %s AND h2.tableName = h.tableName)
|
||||||
|
INNER JOIN HandsPlayers hp ON (h.id = hp.handId)
|
||||||
|
INNER JOIN HandsPlayers hp2 ON (hp2.playerId+0 = hp.playerId+0 AND (hp2.handId = h2.id+0)) /* other hands by these players */
|
||||||
|
INNER JOIN Players p ON (p.id = hp2.PlayerId+0)
|
||||||
|
WHERE hp.handId = %s
|
||||||
|
/* check activeseats once this data returned? (don't want to do that here as it might
|
||||||
|
assume a session ended just because the number of seats dipped for a few hands)
|
||||||
|
*/
|
||||||
|
ORDER BY h.handStart desc, hp2.PlayerId
|
||||||
|
/* order rows by handstart descending so that we can stop reading rows when
|
||||||
|
there's a gap over X minutes between hands (ie. when we get back to start of
|
||||||
|
the session */
|
||||||
|
"""
|
||||||
|
else: # assume postgresql
|
||||||
|
self.query['get_stats_from_hand_session'] = """
|
||||||
|
SELECT hp.playerId AS player_id,
|
||||||
|
hp.handId AS hand_id,
|
||||||
|
hp.seatNo AS seat,
|
||||||
|
p.name AS screen_name,
|
||||||
|
h.seats AS seats,
|
||||||
|
1 AS n,
|
||||||
|
cast(hp2.street0VPI as <signed>integer) AS vpip,
|
||||||
|
cast(hp2.street0Aggr as <signed>integer) AS pfr,
|
||||||
|
cast(hp2.street0_3BChance as <signed>integer) AS TB_opp_0,
|
||||||
|
cast(hp2.street0_3BDone as <signed>integer) AS TB_0,
|
||||||
|
cast(hp2.street1Seen as <signed>integer) AS saw_f,
|
||||||
|
cast(hp2.street1Seen as <signed>integer) AS saw_1,
|
||||||
|
cast(hp2.street2Seen as <signed>integer) AS saw_2,
|
||||||
|
cast(hp2.street3Seen as <signed>integer) AS saw_3,
|
||||||
|
cast(hp2.street4Seen as <signed>integer) AS saw_4,
|
||||||
|
cast(hp2.sawShowdown as <signed>integer) AS sd,
|
||||||
|
cast(hp2.street1Aggr as <signed>integer) AS aggr_1,
|
||||||
|
cast(hp2.street2Aggr as <signed>integer) AS aggr_2,
|
||||||
|
cast(hp2.street3Aggr as <signed>integer) AS aggr_3,
|
||||||
|
cast(hp2.street4Aggr as <signed>integer) AS aggr_4,
|
||||||
|
cast(hp2.otherRaisedStreet1 as <signed>integer) AS was_raised_1,
|
||||||
|
cast(hp2.otherRaisedStreet2 as <signed>integer) AS was_raised_2,
|
||||||
|
cast(hp2.otherRaisedStreet3 as <signed>integer) AS was_raised_3,
|
||||||
|
cast(hp2.otherRaisedStreet4 as <signed>integer) AS was_raised_4,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet1 as <signed>integer) AS f_freq_1,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet2 as <signed>integer) AS f_freq_2,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet3 as <signed>integer) AS f_freq_3,
|
||||||
|
cast(hp2.foldToOtherRaisedStreet4 as <signed>integer) AS f_freq_4,
|
||||||
|
cast(hp2.wonWhenSeenStreet1 as <signed>integer) AS w_w_s_1,
|
||||||
|
cast(hp2.wonAtSD as <signed>integer) AS wmsd,
|
||||||
|
cast(hp2.stealAttemptChance as <signed>integer) AS steal_opp,
|
||||||
|
cast(hp2.stealAttempted as <signed>integer) AS steal,
|
||||||
|
cast(hp2.foldSbToStealChance as <signed>integer) AS SBstolen,
|
||||||
|
cast(hp2.foldedSbToSteal as <signed>integer) AS SBnotDef,
|
||||||
|
cast(hp2.foldBbToStealChance as <signed>integer) AS BBstolen,
|
||||||
|
cast(hp2.foldedBbToSteal as <signed>integer) AS BBnotDef,
|
||||||
|
cast(hp2.street1CBChance as <signed>integer) AS CB_opp_1,
|
||||||
|
cast(hp2.street1CBDone as <signed>integer) AS CB_1,
|
||||||
|
cast(hp2.street2CBChance as <signed>integer) AS CB_opp_2,
|
||||||
|
cast(hp2.street2CBDone as <signed>integer) AS CB_2,
|
||||||
|
cast(hp2.street3CBChance as <signed>integer) AS CB_opp_3,
|
||||||
|
cast(hp2.street3CBDone as <signed>integer) AS CB_3,
|
||||||
|
cast(hp2.street4CBChance as <signed>integer) AS CB_opp_4,
|
||||||
|
cast(hp2.street4CBDone as <signed>integer) AS CB_4,
|
||||||
|
cast(hp2.foldToStreet1CBChance as <signed>integer) AS f_cb_opp_1,
|
||||||
|
cast(hp2.foldToStreet1CBDone as <signed>integer) AS f_cb_1,
|
||||||
|
cast(hp2.foldToStreet2CBChance as <signed>integer) AS f_cb_opp_2,
|
||||||
|
cast(hp2.foldToStreet2CBDone as <signed>integer) AS f_cb_2,
|
||||||
|
cast(hp2.foldToStreet3CBChance as <signed>integer) AS f_cb_opp_3,
|
||||||
|
cast(hp2.foldToStreet3CBDone as <signed>integer) AS f_cb_3,
|
||||||
|
cast(hp2.foldToStreet4CBChance as <signed>integer) AS f_cb_opp_4,
|
||||||
|
cast(hp2.foldToStreet4CBDone as <signed>integer) AS f_cb_4,
|
||||||
|
cast(hp2.totalProfit as <signed>integer) AS net,
|
||||||
|
cast(hp2.street1CheckCallRaiseChance as <signed>integer) AS ccr_opp_1,
|
||||||
|
cast(hp2.street1CheckCallRaiseDone as <signed>integer) AS ccr_1,
|
||||||
|
cast(hp2.street2CheckCallRaiseChance as <signed>integer) AS ccr_opp_2,
|
||||||
|
cast(hp2.street2CheckCallRaiseDone as <signed>integer) AS ccr_2,
|
||||||
|
cast(hp2.street3CheckCallRaiseChance as <signed>integer) AS ccr_opp_3,
|
||||||
|
cast(hp2.street3CheckCallRaiseDone as <signed>integer) AS ccr_3,
|
||||||
|
cast(hp2.street4CheckCallRaiseChance as <signed>integer) AS ccr_opp_4,
|
||||||
|
cast(hp2.street4CheckCallRaiseDone as <signed>integer) AS ccr_4
|
||||||
|
FROM Hands h /* this hand */
|
||||||
|
INNER JOIN Hands h2 ON ( h2.id > %s /* other hands */
|
||||||
|
AND h2.tableName = h.tableName)
|
||||||
|
INNER JOIN HandsPlayers hp ON (h.id = hp.handId) /* players in this hand */
|
||||||
|
INNER JOIN HandsPlayers hp2 ON ( hp2.playerId+0 = hp.playerId+0
|
||||||
|
AND hp2.handId = h2.id) /* other hands by these players */
|
||||||
|
INNER JOIN Players p ON (p.id = hp2.PlayerId+0)
|
||||||
|
WHERE h.id = %s
|
||||||
|
/* check activeseats once this data returned? (don't want to do that here as it might
|
||||||
|
assume a session ended just because the number of seats dipped for a few hands)
|
||||||
|
*/
|
||||||
|
ORDER BY h.handStart desc, hp2.PlayerId
|
||||||
|
/* order rows by handstart descending so that we can stop reading rows when
|
||||||
|
there's a gap over X minutes between hands (ie. when we get back to start of
|
||||||
|
the session */
|
||||||
|
"""
|
||||||
|
|
||||||
self.query['get_players_from_hand'] = """
|
self.query['get_players_from_hand'] = """
|
||||||
SELECT HandsPlayers.playerId, seatNo, name
|
SELECT HandsPlayers.playerId, seatNo, name
|
||||||
|
@ -349,20 +529,13 @@ class Sql:
|
||||||
select
|
select
|
||||||
seatNo AS seat_number,
|
seatNo AS seat_number,
|
||||||
name AS screen_name,
|
name AS screen_name,
|
||||||
card1Value AS card1value,
|
card1, /*card1Value, card1Suit, */
|
||||||
card1Suit AS card1suit,
|
card2, /*card2Value, card2Suit, */
|
||||||
card2Value AS card2value,
|
card3, /*card3Value, card3Suit, */
|
||||||
card2Suit AS card2suit,
|
card4, /*card4Value, card4Suit, */
|
||||||
card3Value AS card3value,
|
card5, /*card5Value, card5Suit, */
|
||||||
card3Suit AS card3suit,
|
card6, /*card6Value, card6Suit, */
|
||||||
card4Value AS card4value,
|
card7 /*card7Value, card7Suit */
|
||||||
card4Suit AS card4suit,
|
|
||||||
card5Value AS card5value,
|
|
||||||
card5Suit AS card5suit,
|
|
||||||
card6Value AS card6value,
|
|
||||||
card6Suit AS card6suit,
|
|
||||||
card7Value AS card7value,
|
|
||||||
card7Suit AS 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
|
||||||
|
@ -389,9 +562,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
|
||||||
|
@ -311,17 +355,38 @@ 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')
|
||||||
|
|
330
pyfpdb/UltimateBetToFpdb.py
Executable file
330
pyfpdb/UltimateBetToFpdb.py
Executable file
|
@ -0,0 +1,330 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2008, Carl Gherardi
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# 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 General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from HandHistoryConverter import *
|
||||||
|
|
||||||
|
|
||||||
|
class UltimateBet(HandHistoryConverter):
|
||||||
|
|
||||||
|
# Static regexes
|
||||||
|
re_GameInfo = re.compile("Stage #(?P<HID>[0-9]+):\s+\(?(?P<GAME>Hold\'em|Razz|Seven Card|Omaha|Omaha Hi/Lo|Badugi) (?P<LIMIT>No Limit|Normal|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)", re.MULTILINE)
|
||||||
|
re_SplitHands = re.compile('(\n\n\n+)')
|
||||||
|
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
|
||||||
|
re_Button = re.compile('Seat #(?P<BUTTON>\d+) is the button', re.MULTILINE)
|
||||||
|
re_PlayerInfo = re.compile('^Seat (?P<SEAT>[0-9]+) - (?P<PNAME>.*) \(\$?(?P<CASH>[.0-9]+) in chips\)', re.MULTILINE)
|
||||||
|
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||||
|
# self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
|
||||||
|
|
||||||
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
|
||||||
|
"""\
|
||||||
|
in_path (default '-' = sys.stdin)
|
||||||
|
out_path (default '-' = sys.stdout)
|
||||||
|
follow : whether to tail -f the input"""
|
||||||
|
HandHistoryConverter.__init__(self, in_path, out_path, sitename="UltimateBet", follow=follow)
|
||||||
|
logging.info("Initialising UltimateBetconverter class")
|
||||||
|
self.filetype = "text"
|
||||||
|
self.codepage = "cp1252"
|
||||||
|
self.siteId = 6 # Needs to match id entry in Sites database
|
||||||
|
if autostart:
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
|
||||||
|
def compilePlayerRegexs(self, hand):
|
||||||
|
players = set([player[1] for player in hand.players])
|
||||||
|
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
||||||
|
# we need to recompile the player regexs.
|
||||||
|
self.compiledPlayers = players
|
||||||
|
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
||||||
|
logging.debug("player_re: " + player_re)
|
||||||
|
self.re_PostSB = re.compile(r"^%s: posts small blind \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
self.re_PostBB = re.compile(r"^%s: posts big blind \$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
self.re_Antes = re.compile(r"^%s - Ante \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
self.re_BringIn = re.compile(r"^%s - Bring-In \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
self.re_PostBoth = re.compile(r"^%s: posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
self.re_HeroCards = re.compile(r"^Dealt to %s - Pocket (\[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
|
||||||
|
self.re_Action = re.compile(r"^%s -(?P<ATYPE> Bets| Checks| raises| Calls| Folds)( \$(?P<BET>[.\d]+))?( to \$(?P<BETTO>[.\d]+))?( (?P<NODISCARDED>\d) cards?( \[(?P<DISCARDED>.+?)\])?)?" % player_re, re.MULTILINE)
|
||||||
|
self.re_ShowdownAction = re.compile(r"^%s - Shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
||||||
|
self.re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*|)" % player_re, re.MULTILINE)
|
||||||
|
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
|
||||||
|
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def readSupportedGames(self):
|
||||||
|
return [ ["ring", "stud", "fl"]
|
||||||
|
]
|
||||||
|
|
||||||
|
def determineGameType(self, handText):
|
||||||
|
info = {'type':'ring'}
|
||||||
|
|
||||||
|
m = self.re_GameInfo.search(handText)
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
|
||||||
|
mg = m.groupdict()
|
||||||
|
|
||||||
|
# translations from captured groups to our info strings
|
||||||
|
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
||||||
|
games = { # base, category
|
||||||
|
"Hold'em" : ('hold','holdem'),
|
||||||
|
'Omaha' : ('hold','omahahi'),
|
||||||
|
'Omaha Hi/Lo' : ('hold','omahahilo'),
|
||||||
|
'Razz' : ('stud','razz'),
|
||||||
|
'7 Card Stud' : ('stud','studhi'),
|
||||||
|
'Badugi' : ('draw','badugi')
|
||||||
|
}
|
||||||
|
currencies = { u'€':'EUR', '$':'USD', '':'T$' }
|
||||||
|
if 'LIMIT' in mg:
|
||||||
|
info['limitType'] = limits[mg['LIMIT']]
|
||||||
|
if 'GAME' in mg:
|
||||||
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
|
if 'SB' in mg:
|
||||||
|
info['sb'] = mg['SB']
|
||||||
|
if 'BB' in mg:
|
||||||
|
info['bb'] = mg['BB']
|
||||||
|
if 'CURRENCY' in mg:
|
||||||
|
info['currency'] = currencies[mg['CURRENCY']]
|
||||||
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
def readHandInfo(self, hand):
|
||||||
|
info = {}
|
||||||
|
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
||||||
|
if m:
|
||||||
|
info.update(m.groupdict())
|
||||||
|
# TODO: Be less lazy and parse maxseats from the HandInfo regex
|
||||||
|
if m.group('TABLEATTRIBUTES'):
|
||||||
|
m2 = re.search("\s*(\d+)-max", m.group('TABLEATTRIBUTES'))
|
||||||
|
hand.maxseats = int(m2.group(1))
|
||||||
|
m = self.re_GameInfo.search(hand.handText)
|
||||||
|
if m: info.update(m.groupdict())
|
||||||
|
m = self.re_Button.search(hand.handText)
|
||||||
|
if m: info.update(m.groupdict())
|
||||||
|
# TODO : I rather like the idea of just having this dict as hand.info
|
||||||
|
logging.debug("readHandInfo: %s" % info)
|
||||||
|
for key in info:
|
||||||
|
if key == 'DATETIME':
|
||||||
|
#2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]
|
||||||
|
#2008/08/17 - 01:14:43 (ET)
|
||||||
|
#2008/09/07 06:23:14 ET
|
||||||
|
m2 = re.search("(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)", info[key])
|
||||||
|
datetime = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), m2.group('M'),m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S'))
|
||||||
|
hand.starttime = time.strptime(datetime, "%Y/%m/%d %H:%M:%S")
|
||||||
|
if key == 'HID':
|
||||||
|
hand.handid = info[key]
|
||||||
|
if key == 'TABLE':
|
||||||
|
hand.tablename = info[key]
|
||||||
|
if key == 'BUTTON':
|
||||||
|
hand.buttonpos = info[key]
|
||||||
|
|
||||||
|
def readButton(self, hand):
|
||||||
|
m = self.re_Button.search(hand.handText)
|
||||||
|
if m:
|
||||||
|
hand.buttonpos = int(m.group('BUTTON'))
|
||||||
|
else:
|
||||||
|
logging.info('readButton: not found')
|
||||||
|
|
||||||
|
def readPlayerStacks(self, hand):
|
||||||
|
logging.debug("readPlayerStacks")
|
||||||
|
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||||
|
players = []
|
||||||
|
for a in m:
|
||||||
|
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||||
|
|
||||||
|
def markStreets(self, hand):
|
||||||
|
# PREFLOP = ** Dealing down cards **
|
||||||
|
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
|
||||||
|
if hand.gametype['base'] in ("hold"):
|
||||||
|
m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
|
||||||
|
r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
|
||||||
|
elif hand.gametype['base'] in ("stud"):
|
||||||
|
m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3rd STREET \*\*\*)|.+)"
|
||||||
|
r"(\*\*\* 3rd STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4th STREET \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* 4th STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5th STREET \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* 5th STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 6th STREET \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* 6th STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* RIVER \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* RIVER \*\*\*(?P<SEVENTH>.+))?", hand.handText,re.DOTALL)
|
||||||
|
elif hand.gametype['base'] in ("draw"):
|
||||||
|
m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)"
|
||||||
|
r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* FIRST DRAW \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* FIRST DRAW \*\*\*(?P<DRAWONE>.+(?=\*\*\* SECOND DRAW \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* SECOND DRAW \*\*\*(?P<DRAWTWO>.+(?=\*\*\* THIRD DRAW \*\*\*)|.+))?"
|
||||||
|
r"(\*\*\* THIRD DRAW \*\*\*(?P<DRAWTHREE>.+))?", hand.handText,re.DOTALL)
|
||||||
|
hand.addStreets(m)
|
||||||
|
|
||||||
|
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
|
||||||
|
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
|
||||||
|
#print "DEBUG readCommunityCards:", street, hand.streets.group(street)
|
||||||
|
m = self.re_Board.search(hand.streets[street])
|
||||||
|
hand.setCommunityCards(street, m.group('CARDS').split(' '))
|
||||||
|
|
||||||
|
def readAntes(self, hand):
|
||||||
|
logging.debug("reading antes")
|
||||||
|
m = self.re_Antes.finditer(hand.handText)
|
||||||
|
for player in m:
|
||||||
|
#~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
|
||||||
|
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
|
||||||
|
|
||||||
|
def readBringIn(self, hand):
|
||||||
|
m = self.re_BringIn.search(hand.handText,re.DOTALL)
|
||||||
|
if m:
|
||||||
|
#~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
|
||||||
|
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
||||||
|
|
||||||
|
def readBlinds(self, hand):
|
||||||
|
try:
|
||||||
|
m = self.re_PostSB.search(hand.handText)
|
||||||
|
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||||
|
except: # no small blind
|
||||||
|
hand.addBlind(None, None, None)
|
||||||
|
for a in self.re_PostBB.finditer(hand.handText):
|
||||||
|
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||||
|
for a in self.re_PostBoth.finditer(hand.handText):
|
||||||
|
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
|
||||||
|
|
||||||
|
def readHeroCards(self, hand):
|
||||||
|
m = self.re_HeroCards.search(hand.handText)
|
||||||
|
if(m == None):
|
||||||
|
#Not involved in hand
|
||||||
|
hand.involved = False
|
||||||
|
else:
|
||||||
|
hand.hero = m.group('PNAME')
|
||||||
|
# "2c, qh" -> set(["2c","qc"])
|
||||||
|
# Also works with Omaha hands.
|
||||||
|
cards = m.group('NEWCARDS')
|
||||||
|
cards = set(cards.split(' '))
|
||||||
|
hand.addHoleCards(cards, m.group('PNAME'))
|
||||||
|
|
||||||
|
def readDrawCards(self, hand, street):
|
||||||
|
logging.debug("readDrawCards")
|
||||||
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
|
if m == None:
|
||||||
|
hand.involved = False
|
||||||
|
else:
|
||||||
|
for player in m:
|
||||||
|
hand.hero = player.group('PNAME') # Only really need to do this once
|
||||||
|
newcards = player.group('NEWCARDS')
|
||||||
|
oldcards = player.group('OLDCARDS')
|
||||||
|
if newcards == None:
|
||||||
|
newcards = set()
|
||||||
|
else:
|
||||||
|
newcards = set(newcards.split(' '))
|
||||||
|
if oldcards == None:
|
||||||
|
oldcards = set()
|
||||||
|
else:
|
||||||
|
oldcards = set(oldcards.split(' '))
|
||||||
|
hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
|
||||||
|
|
||||||
|
|
||||||
|
def readStudPlayerCards(self, hand, street):
|
||||||
|
# See comments of reference implementation in FullTiltToFpdb.py
|
||||||
|
logging.debug("readStudPlayerCards")
|
||||||
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
|
for player in m:
|
||||||
|
#~ logging.debug(player.groupdict())
|
||||||
|
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
|
||||||
|
if oldcards:
|
||||||
|
oldcards = [c.strip() for c in oldcards.split(' ')]
|
||||||
|
if newcards:
|
||||||
|
newcards = [c.strip() for c in newcards.split(' ')]
|
||||||
|
if street=='ANTES':
|
||||||
|
return
|
||||||
|
elif street=='THIRD':
|
||||||
|
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
|
||||||
|
# hero: [xx][o]
|
||||||
|
# others: [o]
|
||||||
|
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
|
||||||
|
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
|
||||||
|
# 4th:
|
||||||
|
# hero: [xxo] [o]
|
||||||
|
# others: [o] [o]
|
||||||
|
# 5th:
|
||||||
|
# hero: [xxoo] [o]
|
||||||
|
# others: [oo] [o]
|
||||||
|
# 6th:
|
||||||
|
# hero: [xxooo] [o]
|
||||||
|
# others: [ooo] [o]
|
||||||
|
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
|
||||||
|
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
|
||||||
|
elif street=='SEVENTH' and newcards:
|
||||||
|
# hero: [xxoooo] [x]
|
||||||
|
# others: not reported.
|
||||||
|
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
|
||||||
|
|
||||||
|
def readAction(self, hand, street):
|
||||||
|
m = self.re_Action.finditer(hand.streets[street])
|
||||||
|
for action in m:
|
||||||
|
if action.group('ATYPE') == ' Raises':
|
||||||
|
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
||||||
|
elif action.group('ATYPE') == ' Calls':
|
||||||
|
hand.addCall( street, action.group('PNAME'), action.group('BET') )
|
||||||
|
elif action.group('ATYPE') == ' Bets':
|
||||||
|
hand.addBet( street, action.group('PNAME'), action.group('BET') )
|
||||||
|
elif action.group('ATYPE') == ' Folds':
|
||||||
|
hand.addFold( street, action.group('PNAME'))
|
||||||
|
elif action.group('ATYPE') == ' Checks':
|
||||||
|
hand.addCheck( street, action.group('PNAME'))
|
||||||
|
#elif action.group('ATYPE') == ' discards':
|
||||||
|
# hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED'))
|
||||||
|
#elif action.group('ATYPE') == ' stands pat':
|
||||||
|
# hand.addStandsPat( street, action.group('PNAME'))
|
||||||
|
else:
|
||||||
|
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
|
||||||
|
|
||||||
|
|
||||||
|
def readShowdownActions(self, hand):
|
||||||
|
for shows in self.re_ShowdownAction.finditer(hand.handText):
|
||||||
|
cards = shows.group('CARDS')
|
||||||
|
cards = set(cards.split(' '))
|
||||||
|
hand.addShownCards(cards, shows.group('PNAME'))
|
||||||
|
|
||||||
|
def readCollectPot(self,hand):
|
||||||
|
for m in self.re_CollectPot.finditer(hand.handText):
|
||||||
|
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
|
||||||
|
|
||||||
|
def readShownCards(self,hand):
|
||||||
|
for m in self.re_ShownCards.finditer(hand.handText):
|
||||||
|
if m.group('CARDS') is not None:
|
||||||
|
cards = m.group('CARDS')
|
||||||
|
cards = set(cards.split(' '))
|
||||||
|
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = OptionParser()
|
||||||
|
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/pokerstars/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt")
|
||||||
|
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
|
||||||
|
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
|
||||||
|
parser.add_option("-q", "--quiet",
|
||||||
|
action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
|
||||||
|
parser.add_option("-v", "--verbose",
|
||||||
|
action="store_const", const=logging.INFO, dest="verbosity")
|
||||||
|
parser.add_option("--vv",
|
||||||
|
action="store_const", const=logging.DEBUG, dest="verbosity")
|
||||||
|
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
LOG_FILENAME = './logging.out'
|
||||||
|
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
|
||||||
|
|
||||||
|
e = UltimateBet(in_path = options.ipath, out_path = options.opath, follow = options.follow)
|
271
pyfpdb/fpdb.py
271
pyfpdb/fpdb.py
|
@ -44,6 +44,7 @@ import GuiPositionalStats
|
||||||
import GuiTableViewer
|
import GuiTableViewer
|
||||||
import GuiAutoImport
|
import GuiAutoImport
|
||||||
import GuiGraphViewer
|
import GuiGraphViewer
|
||||||
|
import GuiSessionViewer
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
import Configuration
|
import Configuration
|
||||||
|
|
||||||
|
@ -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,54 +170,73 @@ 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()
|
if self.obtain_global_lock():
|
||||||
chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
try:
|
||||||
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
||||||
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||||
chooser.set_filename(self.profile)
|
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
||||||
|
chooser.set_filename(self.profile)
|
||||||
|
|
||||||
response = chooser.run()
|
response = chooser.run()
|
||||||
chooser.destroy()
|
chooser.destroy()
|
||||||
if response == gtk.RESPONSE_OK:
|
if response == gtk.RESPONSE_OK:
|
||||||
self.load_profile(chooser.get_filename())
|
self.load_profile(chooser.get_filename())
|
||||||
elif response == gtk.RESPONSE_CANCEL:
|
elif response == gtk.RESPONSE_CANCEL:
|
||||||
print 'User cancelled loading profile'
|
print 'User cancelled loading profile'
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.release_global_lock()
|
||||||
#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()
|
if self.obtain_global_lock():
|
||||||
|
|
||||||
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
|
|
||||||
buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
|
|
||||||
diastring = "Please confirm that you want to (re-)create the tables. If there already are tables in the database "+self.db.database+" on "+self.db.host+" they will be deleted."
|
|
||||||
dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted
|
|
||||||
|
|
||||||
response = dia_confirm.run()
|
lock_released = False
|
||||||
dia_confirm.destroy()
|
try:
|
||||||
if response == gtk.RESPONSE_YES:
|
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
|
||||||
self.db.recreate_tables()
|
buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
|
||||||
elif response == gtk.RESPONSE_NO:
|
diastring = "Please confirm that you want to (re-)create the tables. If there already are tables in the database "+self.db.database+" on "+self.db.host+" they will be deleted."
|
||||||
print 'User cancelled recreating tables'
|
dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted
|
||||||
|
|
||||||
|
response = dia_confirm.run()
|
||||||
|
dia_confirm.destroy()
|
||||||
|
if response == gtk.RESPONSE_YES:
|
||||||
|
if self.db.backend == self.fdb_lock.MYSQL_INNODB:
|
||||||
|
# mysql requires locks on all tables or none - easier to release this lock
|
||||||
|
# than lock all the other tables
|
||||||
|
# ToDo: lock all other tables so that lock doesn't have to be released
|
||||||
|
self.release_global_lock()
|
||||||
|
lock_released = True
|
||||||
|
self.db.recreate_tables()
|
||||||
|
else:
|
||||||
|
# for other dbs use same connection as holds global lock
|
||||||
|
self.fdb_lock.recreate_tables()
|
||||||
|
elif response == gtk.RESPONSE_NO:
|
||||||
|
print 'User cancelled recreating tables'
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if not lock_released:
|
||||||
|
self.release_global_lock()
|
||||||
#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 +263,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', 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,17 +391,25 @@ 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
|
||||||
|
|
||||||
def obtain_global_lock(self):
|
def obtain_global_lock(self):
|
||||||
print "todo: implement obtain_global_lock (users: pls ignore this)"
|
print "\nTaking global lock ..."
|
||||||
|
self.fdb_lock = fpdb_db.fpdb_db()
|
||||||
|
self.fdb_lock.connect(self.settings['db-backend'],
|
||||||
|
self.settings['db-host'],
|
||||||
|
self.settings['db-databaseName'],
|
||||||
|
self.settings['db-user'],
|
||||||
|
self.settings['db-password'])
|
||||||
|
return fpdb_simple.get_global_lock(self.fdb_lock)
|
||||||
#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()
|
||||||
|
@ -305,14 +417,16 @@ class fpdb:
|
||||||
#end def quit_cliecked
|
#end def quit_cliecked
|
||||||
|
|
||||||
def release_global_lock(self):
|
def release_global_lock(self):
|
||||||
print "todo: implement release_global_lock"
|
self.fdb_lock.db.rollback()
|
||||||
|
self.fdb_lock.disconnect()
|
||||||
|
print "Global lock released."
|
||||||
#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 +434,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 +466,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 +475,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 +495,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 +511,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 +523,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,23 +59,33 @@ class fpdb_db:
|
||||||
self.database=database
|
self.database=database
|
||||||
if backend==self.MYSQL_INNODB:
|
if backend==self.MYSQL_INNODB:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
|
try:
|
||||||
|
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
|
||||||
|
except:
|
||||||
|
raise fpdb_simple.FpdbError("MySQL connection failed")
|
||||||
elif backend==self.PGSQL:
|
elif backend==self.PGSQL:
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
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)
|
||||||
|
if self.host and self.user and self.password:
|
||||||
|
try:
|
||||||
|
self.db = psycopg2.connect(host = host,
|
||||||
|
user = user,
|
||||||
|
password = password,
|
||||||
|
database = database)
|
||||||
|
except:
|
||||||
|
raise fpdb_simple.FpdbError("PostgreSQL connection failed")
|
||||||
# For local domain-socket connections, only DB name is
|
# For local domain-socket connections, only DB name is
|
||||||
# needed, and everything else is in fact undefined and/or
|
# needed, and everything else is in fact undefined and/or
|
||||||
# flat out wrong
|
# flat out wrong
|
||||||
if self.host == "localhost" or self.host == "127.0.0.1":
|
|
||||||
self.db = psycopg2.connect(database = database)
|
|
||||||
else:
|
else:
|
||||||
self.db = psycopg2.connect(host = host,
|
try:
|
||||||
user = user,
|
self.db = psycopg2.connect(database = database)
|
||||||
password = password,
|
except:
|
||||||
database = database)
|
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()
|
||||||
|
@ -86,7 +96,7 @@ class fpdb_db:
|
||||||
try:
|
try:
|
||||||
self.cursor.execute("SELECT * FROM Settings")
|
self.cursor.execute("SELECT * FROM Settings")
|
||||||
settings=self.cursor.fetchone()
|
settings=self.cursor.fetchone()
|
||||||
if settings[0]!=118:
|
if settings[0]!=119:
|
||||||
print "outdated or too new database version - please recreate tables"
|
print "outdated or too new database version - please recreate tables"
|
||||||
self.wrongDbVersion=True
|
self.wrongDbVersion=True
|
||||||
except:# _mysql_exceptions.ProgrammingError:
|
except:# _mysql_exceptions.ProgrammingError:
|
||||||
|
@ -137,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'])
|
||||||
|
@ -156,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'])
|
||||||
|
@ -174,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"""
|
||||||
|
@ -191,10 +201,14 @@ class fpdb_db:
|
||||||
#end def get_db_info
|
#end def get_db_info
|
||||||
|
|
||||||
def fillDefaultData(self):
|
def fillDefaultData(self):
|
||||||
self.cursor.execute("INSERT INTO Settings VALUES (118);")
|
self.cursor.execute("INSERT INTO Settings VALUES (119);")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
|
||||||
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Carbon', 'USD');")
|
||||||
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'OnGame', 'USD');")
|
||||||
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'UltimateBet', 'USD');")
|
||||||
|
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Betfair', 'USD');")
|
||||||
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
||||||
#end def fillDefaultData
|
#end def fillDefaultData
|
||||||
|
|
||||||
|
@ -207,4 +221,47 @@ class fpdb_db:
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
print "Finished recreating tables"
|
print "Finished recreating tables"
|
||||||
#end def recreate_tables
|
#end def recreate_tables
|
||||||
#end class fpdb_db
|
|
||||||
|
def getSqlPlayerIDs(names, site_id):
|
||||||
|
result = []
|
||||||
|
notfound = []
|
||||||
|
self.cursor.execute("SELECT name,id FROM Players WHERE name='%s'" % "' OR name='".join(names))
|
||||||
|
tmp = dict(self.cursor.fetchall())
|
||||||
|
for n in names:
|
||||||
|
if n not in tmp:
|
||||||
|
notfound.append(n)
|
||||||
|
else:
|
||||||
|
result.append(tmp[n])
|
||||||
|
if notfound:
|
||||||
|
cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound))
|
||||||
|
cursor.execute("SELECT id FROM Players WHERE name='%s'" % "' OR name='".join(notfound))
|
||||||
|
tmp = cursor.fetchall()
|
||||||
|
for n in tmp:
|
||||||
|
result.append(n[0])
|
||||||
|
|
||||||
|
#We proabably want to cache this
|
||||||
|
return result
|
||||||
|
|
||||||
|
def storeHand(self, p):
|
||||||
|
#stores into table hands:
|
||||||
|
self.cursor.execute ("""INSERT INTO Hands
|
||||||
|
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
|
||||||
|
,playersVpi, playersAtStreet1, playersAtStreet2
|
||||||
|
,playersAtStreet3, playersAtStreet4, playersAtShowdown
|
||||||
|
,street0Raises, street1Raises, street2Raises
|
||||||
|
,street3Raises, street4Raises, street1Pot
|
||||||
|
,street2Pot, street3Pot, street4Pot
|
||||||
|
,showdownPot
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
|
||||||
|
,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats']
|
||||||
|
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
|
||||||
|
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
|
||||||
|
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']
|
||||||
|
,hudCache['street3Raises'], hudCache['street4Raises'], hudCache['street1Pot']
|
||||||
|
,hudCache['street2Pot'], hudCache['street3Pot'], hudCache['street4Pot']
|
||||||
|
,hudCache['showdownPot']
|
||||||
|
)
|
||||||
|
)
|
||||||
|
#return getLastInsertId(backend, conn, cursor)
|
||||||
|
|
|
@ -61,13 +61,14 @@ class Importer:
|
||||||
self.cursor = None
|
self.cursor = None
|
||||||
self.filelist = {}
|
self.filelist = {}
|
||||||
self.dirlist = {}
|
self.dirlist = {}
|
||||||
|
self.siteIds = {}
|
||||||
self.addToDirList = {}
|
self.addToDirList = {}
|
||||||
self.removeFromFileList = {} # to remove deleted files
|
self.removeFromFileList = {} # to remove deleted files
|
||||||
self.monitor = False
|
self.monitor = False
|
||||||
self.updated = {} #Time last import was run {file:mtime}
|
self.updated = {} #Time last import was run {file:mtime}
|
||||||
self.lines = None
|
self.lines = None
|
||||||
self.faobs = None #File as one big string
|
self.faobs = None # File as one big string
|
||||||
self.pos_in_file = {} # dict to remember how far we have read in the file
|
self.pos_in_file = {} # dict to remember how far we have read in the file
|
||||||
#Set defaults
|
#Set defaults
|
||||||
self.callHud = self.config.get_import_parameters().get("callFpdbHud")
|
self.callHud = self.config.get_import_parameters().get("callFpdbHud")
|
||||||
|
|
||||||
|
@ -76,6 +77,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):
|
||||||
|
@ -112,20 +114,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:
|
||||||
|
@ -196,7 +210,7 @@ class Importer:
|
||||||
self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
|
self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
|
||||||
|
|
||||||
for file in self.filelist:
|
for file in self.filelist:
|
||||||
if os.path.exists(file):
|
if os.path.exists(file):
|
||||||
stat_info = os.stat(file)
|
stat_info = os.stat(file)
|
||||||
try:
|
try:
|
||||||
lastupdate = self.updated[file]
|
lastupdate = self.updated[file]
|
||||||
|
@ -215,15 +229,17 @@ 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:
|
||||||
if file in self.filelist:
|
if file in self.filelist:
|
||||||
del self.filelist[file]
|
del self.filelist[file]
|
||||||
|
|
||||||
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):
|
||||||
|
@ -297,10 +313,9 @@ class Importer:
|
||||||
print "TODO: implement importing tournament summaries"
|
print "TODO: implement importing tournament summaries"
|
||||||
#self.faobs = readfile(inputFile)
|
#self.faobs = readfile(inputFile)
|
||||||
#self.parseTourneyHistory()
|
#self.parseTourneyHistory()
|
||||||
return 0
|
return (0,0,0,1,0)
|
||||||
|
|
||||||
site = fpdb_simple.recogniseSite(firstline)
|
category=fpdb_simple.recogniseCategory(firstline)
|
||||||
category = fpdb_simple.recogniseCategory(firstline)
|
|
||||||
|
|
||||||
startpos = 0
|
startpos = 0
|
||||||
stored = 0 #counter
|
stored = 0 #counter
|
||||||
|
@ -308,61 +323,30 @@ 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:
|
|
||||||
#Seat 1: IOS Seat 2: kashman59 (big blind) showed [8c 9d] and won ($3.25) with a pair of Eights
|
if (len(hand)<3):
|
||||||
#ie. Seat X multiple times on the same line in the summary section, when a new player sits down in the
|
|
||||||
#middle of the hand.
|
|
||||||
#TODO: Deal with this properly, either fix the file or make the parsing code work with this line.
|
|
||||||
if "Seat" in hand[i]:
|
|
||||||
mo = re.search(" Seat [0-9]+: ", hand[i])
|
|
||||||
if mo:
|
|
||||||
print "mo=", mo, "\nmo.start=", mo.start(),"\nhand[i]=",hand[i]
|
|
||||||
hand.insert(i+1, hand[i][mo.start()+1:])
|
|
||||||
hand[i] = hand[i][0:mo.start()]
|
|
||||||
|
|
||||||
if len(hand) < 3:
|
|
||||||
pass
|
pass
|
||||||
#todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work.
|
#TODO: This is ugly - we didn't actually find the start of the
|
||||||
elif hand[0].endswith(" (partial)"): #partial hand - do nothing
|
# hand with the outer loop so we test again...
|
||||||
partial += 1
|
else:
|
||||||
elif "Seat" not in hand[1] and "Seat" not in hand[2] and "Seat" not in hand[3]:
|
isTourney=fpdb_simple.isTourney(hand[0])
|
||||||
partial += 1
|
|
||||||
elif cancelled or damaged:
|
|
||||||
partial += 1
|
|
||||||
if damaged:
|
|
||||||
print """
|
|
||||||
DEBUG: Partial hand triggered by a line containing 'Seat X:' twice. This is a
|
|
||||||
bug in the FTP software when a player sits down in the middle of a hand.
|
|
||||||
Adding a newline after the player name will fix the issue
|
|
||||||
"""
|
|
||||||
print "File: %s" %(file)
|
|
||||||
print "Line: %s" %(startpos)
|
|
||||||
else: #normal processing
|
|
||||||
isTourney = fpdb_simple.isTourney(hand[0])
|
|
||||||
if not isTourney:
|
if not isTourney:
|
||||||
hand = fpdb_simple.filterAnteBlindFold(site,hand)
|
hand = fpdb_simple.filterAnteBlindFold(hand)
|
||||||
self.hand=hand
|
self.hand=hand
|
||||||
|
|
||||||
try:
|
try:
|
||||||
handsId = fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db
|
handsId = fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db
|
||||||
,self.fdb.cursor, site, category, hand, self.config)
|
,self.fdb.cursor, self.siteIds[site], category, hand, self.config)
|
||||||
self.fdb.db.commit()
|
self.fdb.db.commit()
|
||||||
|
|
||||||
stored += 1
|
stored += 1
|
||||||
if self.callHud:
|
if self.callHud:
|
||||||
#print "call to HUD here. handsId:",handsId
|
#print "call to HUD here. handsId:",handsId
|
||||||
|
@ -370,37 +354,39 @@ 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)
|
||||||
|
|
||||||
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']:
|
||||||
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
|
||||||
|
|
||||||
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:
|
||||||
|
@ -411,16 +397,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,11 +21,11 @@ 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"
|
||||||
|
|
||||||
#part 0: create the empty arrays
|
#part 0: create the empty arrays
|
||||||
lineTypes = [] #char, valid values: header, name, cards, action, win, rake, ignore
|
lineTypes = [] #char, valid values: header, name, cards, action, win, rake, ignore
|
||||||
lineStreets = [] #char, valid values: (predeal, preflop, flop, turn, river)
|
lineStreets = [] #char, valid values: (predeal, preflop, flop, turn, river)
|
||||||
|
@ -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
|
||||||
|
@ -46,11 +44,9 @@ def mainParser(backend, db, cursor, site, category, hand, config):
|
||||||
smallBlindLine = i
|
smallBlindLine = i
|
||||||
break
|
break
|
||||||
#print "small blind line:",smallBlindLine
|
#print "small blind line:",smallBlindLine
|
||||||
|
|
||||||
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])
|
||||||
|
@ -61,10 +57,10 @@ def mainParser(backend, db, cursor, site, category, hand, config):
|
||||||
rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0])
|
rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0])
|
||||||
|
|
||||||
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(cursor, siteID, buyin, fee, knockout, rebuyOrAddon)
|
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(cursor, siteID, buyin, fee, knockout, rebuyOrAddon)
|
||||||
|
|
||||||
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)
|
||||||
|
@ -74,10 +70,10 @@ def mainParser(backend, db, cursor, site, category, hand, config):
|
||||||
for i, line in enumerate(hand):
|
for i, line in enumerate(hand):
|
||||||
if lineTypes[i] == "name":
|
if lineTypes[i] == "name":
|
||||||
seatLines.append(line)
|
seatLines.append(line)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -141,7 +134,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
|
||||||
hudImportData = fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes
|
hudImportData = fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes
|
||||||
, allIns, actionTypeByNo, winnings, totalWinnings, None
|
, allIns, actionTypeByNo, winnings, totalWinnings, None
|
||||||
, actionTypes, actionAmounts, antes)
|
, actionTypes, actionAmounts, antes)
|
||||||
|
|
||||||
if isTourney:
|
if isTourney:
|
||||||
ranks = map(lambda x: 0, names) # create an array of 0's equal to the length of names
|
ranks = map(lambda x: 0, names) # create an array of 0's equal to the length of names
|
||||||
payin_amounts = fpdb_simple.calcPayin(len(names), buyin, fee)
|
payin_amounts = fpdb_simple.calcPayin(len(names), buyin, fee)
|
||||||
|
@ -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
|
||||||
|
|
|
@ -26,14 +26,13 @@ MYSQL_INNODB = 2
|
||||||
PGSQL = 3
|
PGSQL = 3
|
||||||
SQLITE = 4
|
SQLITE = 4
|
||||||
|
|
||||||
#fastStoreHudCache = False # set this to True to test the new storeHudCache routine
|
fastStoreHudCache = False # set this to True to test the new storeHudCache routine
|
||||||
#
|
|
||||||
#saveActions = True # set this to False to avoid storing action data
|
|
||||||
# # Pros: speeds up imports
|
|
||||||
# # Cons: no action data is saved, so you need to keep the hand histories
|
|
||||||
# # variance not available on stats page
|
|
||||||
# # no graphs
|
|
||||||
|
|
||||||
|
saveActions = True # set this to False to avoid storing action data
|
||||||
|
# Pros: speeds up imports
|
||||||
|
# Cons: no action data is saved, so you need to keep the hand histories
|
||||||
|
# variance not available on stats page
|
||||||
|
# : 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
|
@ -46,7 +46,7 @@ def testGameInfo():
|
||||||
|
|
||||||
|
|
||||||
def testHandInfo():
|
def testHandInfo():
|
||||||
text = u""""PokerStars Game #20461877044: Hold'em No Limit ($1/$2) - 2008/09/16 18:58:01 ET"""
|
text = u"""PokerStars Game #20461877044: Hold'em No Limit ($1/$2) - 2008/09/16 18:58:01 ET"""
|
||||||
hhc = PokerStarsToFpdb.PokerStars(autostart=False)
|
hhc = PokerStarsToFpdb.PokerStars(autostart=False)
|
||||||
h = HoldemOmahaHand(None, "PokerStars", gametype, text, builtFrom = "Test")
|
h = HoldemOmahaHand(None, "PokerStars", gametype, text, builtFrom = "Test")
|
||||||
hhc.readHandInfo(h)
|
hhc.readHandInfo(h)
|
||||||
|
@ -78,4 +78,4 @@ Table 'Caia II' 6-max Seat #2 is the button"""
|
||||||
assert h.buttonpos == '2' # TODO: should this be an int?
|
assert h.buttonpos == '2' # TODO: should this be an int?
|
||||||
assert h.starttime == (2008,11 , 15, 19, 22, 21, 5, 320, -1)
|
assert h.starttime == (2008,11 , 15, 19, 22, 21, 5, 320, -1)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,25 +16,24 @@ def testPokerStarsHHDate():
|
||||||
datetime.datetime(2008,9,7,11,23,14))
|
datetime.datetime(2008,9,7,11,23,14))
|
||||||
)
|
)
|
||||||
|
|
||||||
#def testFullTiltHHDate(self):
|
def testFullTiltHHDate():
|
||||||
# sitngo1 = "Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29"
|
sitngo1 = "Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29"
|
||||||
# cash1 = "Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09"
|
cash1 = "Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09"
|
||||||
# cash2 = "Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13"
|
cash2 = "Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13"
|
||||||
|
|
||||||
# result = fpdb_simple.parseHandStartTime(sitngo1,"ftp")
|
result = fpdb_simple.parseHandStartTime(sitngo1,"ftp")
|
||||||
# self.failUnless(result==datetime.datetime(2009,1,29,05,07,45),
|
assert result==datetime.datetime(2009,1,29,05,07,45)
|
||||||
# "Date incorrect, expected: 2009-01-29 05:07:45 got: " + str(result))
|
result = fpdb_simple.parseHandStartTime(cash1,"ftp")
|
||||||
# result = fpdb_simple.parseHandStartTime(cash1,"ftp")
|
assert result==datetime.datetime(2008,12,9,14,40,20)
|
||||||
# self.failUnless(result==datetime.datetime(2008,12,9,14,40,20),
|
result = fpdb_simple.parseHandStartTime(cash2,"ftp")
|
||||||
# "Date incorrect, expected: 2008-12-09 14:40:20 got: " + str(result))
|
assert result==datetime.datetime(2008,12,13,10,9,36)
|
||||||
# result = fpdb_simple.parseHandStartTime(cash2,"ftp")
|
|
||||||
# self.failUnless(result==datetime.datetime(2008,12,13,10,9,36),
|
|
||||||
# "Date incorrect, expected: 2008-12-13 10:09:36 got: " + str(result))
|
|
||||||
|
|
||||||
# def testTableDetection(self):
|
def testTableDetection():
|
||||||
# result = Tables.clean_title("French (deep)")
|
result = Tables.clean_title("French (deep)")
|
||||||
# self.failUnless(result == "French", "French (deep) parsed incorrectly. Expected 'French' got: " + str(result))
|
assert result == "French"
|
||||||
# result = ("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
|
result = Tables.clean_title("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
|
||||||
|
assert result == "French"
|
||||||
|
|
||||||
|
for (header, site, result) in tuples:
|
||||||
|
yield checkDateParse, header, site, result
|
||||||
|
|
||||||
for (header, site, result) in tuples:
|
|
||||||
yield checkDateParse, header, site, result
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user