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

This commit is contained in:
Mika Bostrom 2009-08-09 14:27:05 +03:00
commit b1281385ec
21 changed files with 3574 additions and 2872 deletions

View File

@ -94,29 +94,32 @@ def cardFromValueSuit(value, suit):
elif suit == 's': return(value+38) elif suit == 's': return(value+38)
else: return(0) else: return(0)
def valueSuitFromCard(card): suitFromCardList = ['', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah'
""" 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 or not card:
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' , '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' , '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' , '2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks', 'As'
][card] ) ]
def valueSuitFromCard(card):
""" Function to convert a card stored in the database (int 0-52) into value
and suit like 9s, 4c etc """
global suitFromCardList
if card < 0 or card > 52 or not card:
return('')
else:
return suitFromCardList[card]
def encodeCard(cardString): encodeCardList = {'2h': 1, '3h': 2, '4h': 3, '5h': 4, '6h': 5, '7h': 6, '8h': 7, '9h': 8, 'Th': 9, 'Jh': 10, 'Qh': 11, 'Kh': 12, 'Ah': 13,
"""Take a card string (Ah) and convert it to the db card code (1)."""
try:
return {'2h': 1, '3h': 2, '4h': 3, '5h': 4, '6h': 5, '7h': 6, '8h': 7, '9h': 8, 'Th': 9, 'Jh': 10, 'Qh': 11, 'Kh': 12, 'Ah': 13,
'2d': 14, '3d': 15, '4d': 16, '5d': 17, '6d': 18, '7d': 19, '8d': 20, '9d': 21, 'Td': 22, 'Jd': 23, 'Qd': 24, 'Kd': 25, 'Ad': 26, '2d': 14, '3d': 15, '4d': 16, '5d': 17, '6d': 18, '7d': 19, '8d': 20, '9d': 21, 'Td': 22, 'Jd': 23, 'Qd': 24, 'Kd': 25, 'Ad': 26,
'2c': 27, '3c': 28, '4c': 29, '5c': 30, '6c': 31, '7c': 32, '8c': 33, '9c': 34, 'Tc': 35, 'Jc': 36, 'Qc': 27, 'Kc': 38, 'Ac': 39, '2c': 27, '3c': 28, '4c': 29, '5c': 30, '6c': 31, '7c': 32, '8c': 33, '9c': 34, 'Tc': 35, 'Jc': 36, 'Qc': 27, 'Kc': 38, 'Ac': 39,
'2s': 40, '3s': 41, '4s': 42, '5s': 43, '6s': 44, '7s': 45, '8s': 46, '9s': 47, 'Ts': 48, 'Js': 49, 'Qs': 50, 'Ks': 51, 'As': 52, '2s': 40, '3s': 41, '4s': 42, '5s': 43, '6s': 44, '7s': 45, '8s': 46, '9s': 47, 'Ts': 48, 'Js': 49, 'Qs': 50, 'Ks': 51, 'As': 52,
' ': 0 ' ': 0
}[cardString] }
except:
return 0 # everthing that isn't known is a unknown! def encodeCard(cardString):
"""Take a card string (Ah) and convert it to the db card code (1)."""
global encodeCardList
if cardString not in encodeCardList: return 0
return encodeCardList[cardString]
if __name__ == '__main__': if __name__ == '__main__':
print "fpdb card encoding(same as pokersource)" print "fpdb card encoding(same as pokersource)"

View File

@ -27,10 +27,11 @@ Create and manage the database objects.
import sys import sys
import traceback import traceback
from datetime import datetime, date, time, timedelta from datetime import datetime, date, time, timedelta
from time import time, strftime from time import time, strftime, sleep
import string import string
import re import re
import logging import logging
import Queue
# pyGTK modules # pyGTK modules
@ -67,7 +68,7 @@ class Database:
, {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09 , {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09
, {'tab':'HandsPlayers', 'col':'handId', 'drop':0} # not needed, handled by fk , {'tab':'HandsPlayers', 'col':'handId', 'drop':0} # not needed, handled by fk
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} # not needed, handled by fk , {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} # not needed, handled by fk
, {'tab':'HandsPlayers', 'col':'tourneysTypeId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
] ]
@ -181,57 +182,24 @@ class Database:
else: else:
self.sql = sql self.sql = sql
self.pcache = None # PlayerId cache
self.cachemiss = 0 # Delete me later - using to count player cache misses
self.cachehit = 0 # Delete me later - using to count player cache hits
# config while trying out new hudcache mechanism # config while trying out new hudcache mechanism
self.use_date_in_hudcache = True self.use_date_in_hudcache = True
# To add to config: #self.hud_hero_style = 'T' # Duplicate set of vars just for hero - not used yet.
self.hud_session_gap = 30 # Gap (minutes) between hands that indicates a change of session #self.hud_hero_hands = 2000 # Idea is that you might want all-time stats for others
# (hands every 2 mins for 1 hour = one session, if followed #self.hud_hero_days = 30 # but last T days or last H hands for yourself
# by a 40 minute gap and then more hands on same table that is
# a new session)
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 = 2000 # Max number of hands from each player to use for hud stats
self.hud_days = 30 # Max number of days from each player to use for hud stats
self.hud_hero_style = 'T' # Duplicate set of vars just for hero # vars for hand ids or dates fetched according to above config:
self.hud_hero_hands = 2000 self.hand_1day_ago = 0 # max hand id more than 24 hrs earlier than now
self.hud_hero_days = 30 self.date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD)
self.date_nhands_ago = {} # dates N hands ago per player - not used yet
self.cursor = self.fdb.cursor self.cursor = self.fdb.cursor
if self.fdb.wrongDbVersion == False:
# self.hand_1day_ago used to fetch stats for current session (i.e. if hud_style = 'S')
self.hand_1day_ago = 0
self.cursor.execute(self.sql.query['get_hand_1day_ago'])
row = self.cursor.fetchone()
if row and row[0]:
self.hand_1day_ago = row[0]
#print "hand 1day ago =", self.hand_1day_ago
# self.date_ndays_ago used if hud_style = 'T'
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 is used for fetching stats for last n hands (hud_style = 'H')
# This option not used yet
self.hand_nhands_ago = 0
# should use aggregated version of query if appropriate
self.cursor.execute(self.sql.query['get_hand_nhands_ago'], (self.hud_hands,self.hud_hands))
row = self.cursor.fetchone()
if row and row[0]:
self.hand_nhands_ago = row[0]
print "hand n hands ago =", self.hand_nhands_ago
#self.cursor.execute(self.sql.query['get_table_name'], (hand_id, ))
#row = self.cursor.fetchone()
else:
print "Bailing on DB query, not sure it exists yet"
self.saveActions = False if self.import_options['saveActions'] == False else True self.saveActions = False if self.import_options['saveActions'] == False else True
self.connection.rollback() # make sure any locks taken so far are released self.connection.rollback() # make sure any locks taken so far are released
@ -364,22 +332,67 @@ class Database:
winners[row[0]] = row[1] winners[row[0]] = row[1]
return winners return winners
def get_stats_from_hand(self, hand, aggregate = False): def init_hud_stat_vars(self, hud_days):
if self.hud_style == 'S': """Initialise variables used by Hud to fetch stats."""
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': try:
# self.hand_1day_ago used to fetch stats for current session (i.e. if hud_style = 'S')
self.hand_1day_ago = 1
c = self.get_cursor()
c.execute(self.sql.query['get_hand_1day_ago'])
row = c.fetchone()
if row and row[0]:
self.hand_1day_ago = row[0]
#print "hand 1day ago =", self.hand_1day_ago
# self.date_ndays_ago used if hud_style = 'T'
d = timedelta(days=hud_days)
now = datetime.utcnow() - d
self.date_ndays_ago = "d%02d%02d%02d" % (now.year-2000, now.month, now.day)
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
def init_player_hud_stat_vars(self, playerid):
# not sure if this is workable, to be continued ...
try:
# self.date_nhands_ago is used for fetching stats for last n hands (hud_style = 'H')
# This option not used yet - needs to be called for each player :-(
self.date_nhands_ago[str(playerid)] = 'd000000'
# should use aggregated version of query if appropriate
c.execute(self.sql.query['get_date_nhands_ago'], (self.hud_hands, playerid))
row = c.fetchone()
if row and row[0]:
self.date_nhands_ago[str(playerid)] = row[0]
c.close()
print "date n hands ago = " + self.date_nhands_ago[str(playerid)] + "(playerid "+str(playerid)+")"
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
def get_stats_from_hand(self, hand, aggregate = False, hud_style = 'A', agg_bb_mult = 100):
if hud_style == 'S':
return( self.get_stats_from_hand_session(hand) )
else: # hud_style == A
if hud_style == 'T':
stylekey = self.date_ndays_ago stylekey = self.date_ndays_ago
#elif hud_style == 'H':
# stylekey = date_nhands_ago needs array by player here ...
else: # assume A (all-time) else: # assume A (all-time)
stylekey = '0000000' # all stylekey values should be higher than this stylekey = '0000000' # all stylekey values should be higher than this
subs = (hand, hand, stylekey) if aggregate:
#print "get stats: hud style =", self.hud_style, "subs =", subs query = 'get_stats_from_hand_aggregated'
subs = (hand, stylekey, agg_bb_mult, agg_bb_mult)
else:
query = 'get_stats_from_hand'
subs = (hand, stylekey)
#print "get stats: hud style =", hud_style, "query =", query, "subs =", subs
c = self.connection.cursor() c = self.connection.cursor()
# now get the stats # now get the stats
@ -398,17 +411,14 @@ class Database:
# uses query on handsplayers instead of hudcache to get stats on just this session # uses query on handsplayers instead of hudcache to get stats on just this session
def get_stats_from_hand_session(self, hand): def get_stats_from_hand_session(self, hand):
if self.hud_style == 'S':
query = self.sql.query['get_stats_from_hand_session'] query = self.sql.query['get_stats_from_hand_session']
if self.db_server == 'mysql': if self.db_server == 'mysql':
query = query.replace("<signed>", 'signed ') query = query.replace("<signed>", 'signed ')
else: else:
query = query.replace("<signed>", '') query = query.replace("<signed>", '')
else: # self.hud_style == A
return None
subs = (self.hand_1day_ago, hand) subs = (self.hand_1day_ago, hand)
c = self.connection.cursor() c = self.get_cursor()
# now get the stats # now get the stats
#print "sess_stats: subs =", subs, "subs[0] =", subs[0] #print "sess_stats: subs =", subs, "subs[0] =", subs[0]
@ -442,7 +452,7 @@ class Database:
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_name, site))
row = c.fetchone() row = c.fetchone()
if row: if row:
return row[0] return row[0]
@ -469,7 +479,6 @@ class Database:
,action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName ,action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName
,seatNos): ,seatNos):
try:
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
hands_id = self.storeHands(self.backend, site_hand_no, gametype_id hands_id = self.storeHands(self.backend, site_hand_no, gametype_id
@ -483,9 +492,6 @@ class Database:
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop': if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
self.storeHudCache(self.backend, base, category, gametype_id, hand_start_time, player_ids, hudImportData) self.storeHudCache(self.backend, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
except:
print "ring_stud error: " + str(sys.exc_value) # in case exception doesn't get printed
raise fpdb_simple.FpdbError("ring_stud error: " + str(sys.exc_value))
return hands_id return hands_id
#end def ring_stud #end def ring_stud
@ -496,7 +502,6 @@ class Database:
,action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): ,action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a holdem/omaha hand into the database""" """stores a holdem/omaha hand into the database"""
try:
t0 = time() t0 = time()
#print "in ring_holdem_omaha" #print "in ring_holdem_omaha"
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
@ -517,9 +522,6 @@ class Database:
self.storeHudCache(self.backend, base, category, gametype_id, hand_start_time, player_ids, hudImportData) self.storeHudCache(self.backend, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
t5 = time() t5 = time()
#print "fills=(%4.3f) saves=(%4.3f,%4.3f,%4.3f)" % (t2-t0, t3-t2, t4-t3, t5-t4) #print "fills=(%4.3f) saves=(%4.3f,%4.3f,%4.3f)" % (t2-t0, t3-t2, t4-t3, t5-t4)
except:
print "ring_holdem_omaha error: " + str(sys.exc_value) # in case exception doesn't get printed
raise fpdb_simple.FpdbError("ring_holdem_omaha error: " + str(sys.exc_value))
return hands_id return hands_id
#end def ring_holdem_omaha #end def ring_holdem_omaha
@ -532,7 +534,6 @@ class Database:
,actionNos, hudImportData, maxSeats, tableName, seatNos): ,actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a tourney holdem/omaha hand into the database""" """stores a tourney holdem/omaha hand into the database"""
try:
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
fpdb_simple.fill_board_cards(board_values, board_suits) fpdb_simple.fill_board_cards(board_values, board_suits)
@ -551,9 +552,6 @@ class Database:
#print "tourney holdem, backend=%d" % backend #print "tourney holdem, backend=%d" % backend
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop': if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
self.storeHudCache(self.backend, base, category, gametype_id, hand_start_time, player_ids, hudImportData) self.storeHudCache(self.backend, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
except:
print "tourney_holdem_omaha error: " + str(sys.exc_value) # in case exception doesn't get printed
raise fpdb_simple.FpdbError("tourney_holdem_omaha error: " + str(sys.exc_value))
return hands_id return hands_id
#end def tourney_holdem_omaha #end def tourney_holdem_omaha
@ -565,7 +563,6 @@ class Database:
,actionNos, hudImportData, maxSeats, tableName, seatNos): ,actionNos, hudImportData, maxSeats, tableName, seatNos):
#stores a tourney stud/razz hand into the database #stores a tourney stud/razz hand into the database
try:
fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits) fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits)
tourney_id = self.store_tourneys(tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime) tourney_id = self.store_tourneys(tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime)
@ -582,9 +579,6 @@ class Database:
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop': if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
self.storeHudCache(self.backend, base, category, gametypeId, hand_start_time, playerIds, hudImportData) self.storeHudCache(self.backend, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
except:
print "tourney_stud error: " + str(sys.exc_value) # in case exception doesn't get printed
raise fpdb_simple.FpdbError("tourney_stud error: " + str(sys.exc_value))
return hands_id return hands_id
#end def tourney_stud #end def tourney_stud
@ -613,7 +607,7 @@ class Database:
"AND referenced_column_name = %s ", "AND referenced_column_name = %s ",
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) ) (fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
cons = c.fetchone() cons = c.fetchone()
print "preparebulk find fk: cons=", cons #print "preparebulk find fk: cons=", cons
if cons: if cons:
print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol'] print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol']
try: try:
@ -740,8 +734,8 @@ class Database:
if self.backend == self.MYSQL_INNODB: if self.backend == self.MYSQL_INNODB:
print "creating mysql index ", idx['tab'], idx['col'] print "creating mysql index ", idx['tab'], idx['col']
try: try:
c.execute( "alter table %s add index %s(%s)" s = "alter table %s add index %s(%s)" % (idx['tab'],idx['col'],idx['col'])
, (idx['tab'],idx['col'],idx['col']) ) c.execute(s)
except: except:
print " create fk failed: " + str(sys.exc_info()) print " create fk failed: " + str(sys.exc_info())
elif self.backend == self.PGSQL: elif self.backend == self.PGSQL:
@ -749,9 +743,8 @@ class Database:
# mod to use tab_col for index name? # mod to use tab_col for index name?
print "creating pg index ", idx['tab'], idx['col'] print "creating pg index ", idx['tab'], idx['col']
try: try:
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col']) s = "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
c.execute( "create index %s_%s_idx on %s(%s)" c.execute(s)
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
except: except:
print " create index failed: " + str(sys.exc_info()) print " create index failed: " + str(sys.exc_info())
else: else:
@ -820,9 +813,11 @@ class Database:
self.fillDefaultData() self.fillDefaultData()
self.commit() self.commit()
except: except:
print "Error creating tables: ", str(sys.exc_value) #print "Error creating tables: ", str(sys.exc_value)
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error creating tables: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
self.rollback() self.rollback()
raise fpdb_simple.FpdbError( "Error creating tables " + str(sys.exc_value) ) raise
#end def disconnect #end def disconnect
def drop_tables(self): def drop_tables(self):
@ -852,8 +847,9 @@ class Database:
self.commit() self.commit()
except: except:
print "Error dropping tables: " + str(sys.exc_value) print "***Error dropping tables: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
raise fpdb_simple.FpdbError( "Error dropping tables " + str(sys.exc_value) ) self.rollback()
raise
#end def drop_tables #end def drop_tables
def createAllIndexes(self): def createAllIndexes(self):
@ -866,20 +862,18 @@ class Database:
if self.backend == self.MYSQL_INNODB: if self.backend == self.MYSQL_INNODB:
print "creating mysql index ", idx['tab'], idx['col'] print "creating mysql index ", idx['tab'], idx['col']
try: try:
self.get_cursor().execute( "alter table %s add index %s(%s)" s = "create index %s on %s(%s)" % (idx['col'],idx['tab'],idx['col'])
, (idx['tab'],idx['col'],idx['col']) ) self.get_cursor().execute(s)
except: except:
pass print " create idx failed: " + str(sys.exc_info())
elif self.backend == self.PGSQL: elif self.backend == self.PGSQL:
# mod to use tab_col for index name? # mod to use tab_col for index name?
print "creating pg index ", idx['tab'], idx['col'] print "creating pg index ", idx['tab'], idx['col']
try: try:
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col']) s = "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
self.get_cursor().execute( "create index %s_%s_idx on %s(%s)" self.get_cursor().execute(s)
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
except: except:
print " ERROR! :-(" print " create idx failed: " + str(sys.exc_info())
pass
else: else:
print "Only MySQL and Postgres supported so far" print "Only MySQL and Postgres supported so far"
return -1 return -1
@ -926,6 +920,9 @@ class Database:
c.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')") c.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('Everleaf', 'USD')") c.execute("INSERT INTO Sites (name,currency) VALUES ('Everleaf', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('Win2day', 'USD')") c.execute("INSERT INTO Sites (name,currency) VALUES ('Win2day', 'USD')")
if self.backend == self.SQLITE:
c.execute("INSERT INTO TourneyTypes VALUES (NULL, 1, 0, 0, 0, 0);")
else:
c.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);") c.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
#c.execute("""INSERT INTO TourneyTypes #c.execute("""INSERT INTO TourneyTypes
# (siteId,buyin,fee,knockout,rebuyOrAddon) VALUES # (siteId,buyin,fee,knockout,rebuyOrAddon) VALUES
@ -979,6 +976,35 @@ class Database:
print "Error during fdb.lock_for_insert:", str(sys.exc_value) print "Error during fdb.lock_for_insert:", str(sys.exc_value)
#end def lock_for_insert #end def lock_for_insert
def getSqlPlayerIDs(self, pnames, siteid):
result = {}
if(self.pcache == None):
self.pcache = LambdaDict(lambda key:self.insertPlayer(key, siteid))
for player in pnames:
result[player] = self.pcache[player]
# NOTE: Using the LambdaDict does the same thing as:
#if player in self.pcache:
# #print "DEBUG: cachehit"
# pass
#else:
# self.pcache[player] = self.insertPlayer(player, siteid)
#result[player] = self.pcache[player]
return result
def insertPlayer(self, name, site_id):
result = None
c = self.get_cursor()
c.execute ("SELECT id FROM Players WHERE name=%s", (name,))
tmp=c.fetchall()
if (len(tmp)==0): #new player
c.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (name, site_id))
#Get last id might be faster here.
c.execute ("SELECT id FROM Players WHERE name=%s", (name,))
tmp=c.fetchall()
return tmp[0][0]
def store_the_hand(self, h): def store_the_hand(self, h):
"""Take a HandToWrite object and store it in the db""" """Take a HandToWrite object and store it in the db"""
@ -994,7 +1020,7 @@ class Database:
result = self.tourney_holdem_omaha( result = self.tourney_holdem_omaha(
h.config, h.settings, h.base, h.category, h.siteTourneyNo, h.buyin h.config, h.settings, h.base, h.category, h.siteTourneyNo, h.buyin
, h.fee, h.knockout, h.entries, h.prizepool, h.tourneyStartTime , h.fee, h.knockout, h.entries, h.prizepool, h.tourneyStartTime
, h.payin_amounts, h.ranks, h.tourneyTypeId, h.siteID, h.siteHandNo , payin_amounts, ranks, h.tourneyTypeId, h.siteID, h.siteHandNo
, h.gametypeID, h.handStartTime, h.names, h.playerIDs, h.startCashes , h.gametypeID, h.handStartTime, h.names, h.playerIDs, h.startCashes
, h.positions, h.cardValues, h.cardSuits, h.boardValues, h.boardSuits , h.positions, h.cardValues, h.cardSuits, h.boardValues, h.boardSuits
, h.winnings, h.rakes, h.actionTypes, h.allIns, h.actionAmounts , h.winnings, h.rakes, h.actionTypes, h.allIns, h.actionAmounts
@ -1003,13 +1029,13 @@ class Database:
result = self.tourney_stud( result = self.tourney_stud(
h.config, h.settings, h.base, h.category, h.siteTourneyNo h.config, h.settings, h.base, h.category, h.siteTourneyNo
, h.buyin, h.fee, h.knockout, h.entries, h.prizepool, h.tourneyStartTime , h.buyin, h.fee, h.knockout, h.entries, h.prizepool, h.tourneyStartTime
, h.payin_amounts, h.ranks, h.tourneyTypeId, h.siteID, h.siteHandNo , payin_amounts, ranks, h.tourneyTypeId, h.siteID, h.siteHandNo
, h.gametypeID, h.handStartTime, h.names, h.playerIDs, h.startCashes , h.gametypeID, h.handStartTime, h.names, h.playerIDs, h.startCashes
, h.antes, h.cardValues, h.cardSuits, h.winnings, h.rakes, h.actionTypes , h.antes, h.cardValues, h.cardSuits, h.winnings, h.rakes, h.actionTypes
, h.allIns, h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats , h.allIns, h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats
, h.tableName, h.seatNos) , h.tableName, h.seatNos)
else: else:
raise fpself.simple.Fpself.rror("unrecognised category") raise fpdb_simple.FpdbError("unrecognised category")
else: else:
if h.base == "hold": if h.base == "hold":
result = self.ring_holdem_omaha( result = self.ring_holdem_omaha(
@ -1027,11 +1053,13 @@ class Database:
, h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats, h.tableName , h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats, h.tableName
, h.seatNos) , h.seatNos)
else: else:
raise fpself.simple.Fpself.rror ("unrecognised category") raise fpdb_simple.FpdbError("unrecognised category")
self.commit()
except: except:
print "Error storing hand: " + str(sys.exc_value) print "Error storing hand: " + str(sys.exc_value)
self.rollback() self.rollback()
# re-raise the exception so that the calling routine can decide what to do:
# (e.g. a write thread might try again)
raise
return result return result
#end def store_the_hand #end def store_the_hand
@ -1576,8 +1604,85 @@ class Database:
#end def store_tourneys_players #end def store_tourneys_players
# read HandToWrite objects from q and insert into database
def insert_queue_hands(self, q, maxwait=10, commitEachHand=True):
n,fails,maxTries,firstWait = 0,0,4,0.1
sendFinal = False
t0 = time()
while True:
try:
h = q.get(True) # (True,maxWait) has probs if 1st part of import is all dups
except Queue.Empty:
# Queue.Empty exception thrown if q was empty for
# if q.empty() also possible - no point if testing for Queue.Empty exception
# maybe increment a counter and only break after a few times?
# could also test threading.active_count() or look through threading.enumerate()
# so break immediately if no threads, but count up to X exceptions if a writer
# thread is still alive???
print "queue empty too long - writer stopping ..."
break
except:
print "writer stopping, error reading queue: " + str(sys.exc_info())
break
#print "got hand", str(h.get_finished())
tries,wait,again = 0,firstWait,True
while again:
try:
again = False # set this immediately to avoid infinite loops!
if h.get_finished():
# all items on queue processed
sendFinal = True
else:
self.store_the_hand(h)
# optional commit, could be every hand / every N hands / every time a
# commit message received?? mark flag to indicate if commits outstanding
if commitEachHand:
self.commit()
n = n + 1
except:
#print "iqh store error", sys.exc_value # debug
self.rollback()
if re.search('deadlock', str(sys.exc_info()[1]), re.I):
# deadlocks only a problem if hudcache is being updated
tries = tries + 1
if tries < maxTries and wait < 5: # wait < 5 just to make sure
print "deadlock detected - trying again ..."
sleep(wait)
wait = wait + wait
again = True
else:
print "too many deadlocks - failed to store hand " + h.get_siteHandNo()
if not again:
fails = fails + 1
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error storing hand: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
# finished trying to store hand
# always reduce q count, whether or not this hand was saved ok
q.task_done()
# while True loop
self.commit()
if sendFinal:
q.task_done()
print "db writer finished: stored %d hands (%d fails) in %.1f seconds" % (n, fails, time()-t0)
# end def insert_queue_hands():
def send_finish_msg(self, q):
try:
h = HandToWrite(True)
q.put(h)
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error sending finish: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
# end def send_finish_msg():
# Class used to hold all the data needed to write a hand to the db # Class used to hold all the data needed to write a hand to the db
# mainParser() in fpdb_parse_logic.py creates one of these and then passes it to # mainParser() in fpdb_parse_logic.py creates one of these and then passes it to
# self.insert_queue_hands()
class HandToWrite: class HandToWrite:
@ -1676,49 +1781,6 @@ class HandToWrite:
raise raise
# end def set_hand # end def set_hand
def set_ring_holdem_omaha( self, config, settings, base, category, siteHandNo
, gametypeID, handStartTime, names, playerIDs
, startCashes, positions, cardValues, cardSuits
, boardValues, boardSuits, winnings, rakes
, actionTypes, allIns, actionAmounts, actionNos
, hudImportData, maxSeats, tableName, seatNos ):
self.config = config
self.settings = settings
self.base = base
self.category = category
self.siteHandNo = siteHandNo
self.gametypeID = gametypeID
self.handStartTime = handStartTime
self.names = names
self.playerIDs = playerIDs
self.startCashes = startCashes
self.positions = positions
self.cardValues = cardValues
self.cardSuits = cardSuits
self.boardValues = boardValues
self.boardSuits = boardSuits
self.winnings = winnings
self.rakes = rakes
self.actionTypes = actionTypes
self.allIns = allIns
self.actionAmounts = actionAmounts
self.actionNos = actionNos
self.hudImportData = hudImportData
self.maxSeats = maxSeats
self.tableName = tableName
self.seatNos = seatNos
# end def set_ring_holdem_omaha
def send_ring_holdem_omaha(self, db):
result = db.ring_holdem_omaha(
self.config, self.settings, self.base, self.category, self.siteHandNo
, self.gametypeID, self.handStartTime, self.names, self.playerIDs
, self.startCashes, self.positions, self.cardValues, self.cardSuits
, self.boardValues, self.boardSuits, self.winnings, self.rakes
, self.actionTypes, self.allIns, self.actionAmounts, self.actionNos
, self.hudImportData, self.maxSeats, self.tableName, self.seatNos)
# end def send_ring_holdem_omaha
def get_finished(self): def get_finished(self):
return( self.finished ) return( self.finished )
# end def get_finished # end def get_finished
@ -1738,6 +1800,8 @@ if __name__=="__main__":
print "database connection object = ", db_connection.connection print "database connection object = ", db_connection.connection
print "database type = ", db_connection.type print "database type = ", db_connection.type
db_connection.recreate_tables()
h = db_connection.get_last_hand() h = db_connection.get_last_hand()
print "last hand = ", h print "last hand = ", h
@ -1759,3 +1823,17 @@ if __name__=="__main__":
print "press enter to continue" print "press enter to continue"
sys.stdin.readline() sys.stdin.readline()
#Code borrowed from http://push.cx/2008/caching-dictionaries-in-python-vs-ruby
class LambdaDict(dict):
def __init__(self, l):
super(LambdaDict, self).__init__()
self.l = l
def __getitem__(self, key):
if key in self:
return self.get(key)
else:
self.__setitem__(key, self.l(key))
return self.get(key)

View File

@ -15,6 +15,9 @@
#In the "official" distribution you can find the license in #In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
#fpdb modules
import Card
class DerivedStats(): class DerivedStats():
def __init__(self, hand): def __init__(self, hand):
self.hand = hand self.hand = hand
@ -89,5 +92,69 @@ class DerivedStats():
self.street4CheckCallRaiseChance = 0 self.street4CheckCallRaiseChance = 0
self.street4CheckCallRaiseDone = 0 self.street4CheckCallRaiseDone = 0
def getStats(): self.hands = {}
self.handsplayers = {}
def getStats(self, hand):
for player in hand.players:
self.handsplayers[player[1]] = {}
self.assembleHands(self.hand)
self.assembleHandsPlayers(self.hand)
print "hands =", self.hands
print "handsplayers =", self.handsplayers
def assembleHands(self, hand):
self.hands['tableName'] = hand.tablename
self.hands['siteHandNo'] = hand.handid
self.hands['gametypeId'] = None # Leave None, handled later after checking db
self.hands['handStart'] = hand.starttime # format this!
self.hands['importTime'] = None
self.hands['seats'] = self.countPlayers(hand)
self.hands['maxSeats'] = hand.maxseats
self.hands['boardcard1'] = None
self.hands['boardcard2'] = None
self.hands['boardcard3'] = None
self.hands['boardcard4'] = None
self.hands['boardcard5'] = None
boardCard = 1
for street in hand.communityStreets:
for card in hand.board[street]:
self.hands['boardcard%s' % str(boardCard)] = Card.encodeCard(card)
boardCard += 1
def assembleHandsPlayers(self, hand):
self.vpip(self.hand)
for i, street in enumerate(hand.actionStreets[1:]):
self.aggr(self.hand, i)
def vpip(self, hand):
vpipers = set()
for act in hand.actions[hand.actionStreets[1]]:
if act[1] in ('calls','bets', 'raises'):
vpipers.add(act[0])
for player in hand.players:
if player[1] in vpipers:
self.handsplayers[player[1]]['vpip'] = True
else:
self.handsplayers[player[1]]['vpip'] = False
self.hands['playersVpi'] = len(vpipers)
def aggr(self, hand, i):
aggrers = set()
for act in hand.actions[hand.actionStreets[i]]:
if act[1] in ('completes', 'raises'):
aggrers.add(act[0])
for player in hand.players:
if player[1] in aggrers:
self.handsplayers[player[1]]['street%sAggr' % i] = True
else:
self.handsplayers[player[1]]['street%sAggr' % i] = False
def countPlayers(self, hand):
pass pass

View File

@ -31,10 +31,11 @@ import Configuration
import string import string
class GuiAutoImport (threading.Thread): class GuiAutoImport (threading.Thread):
def __init__(self, settings, config): def __init__(self, settings, config, sql):
"""Constructor for GuiAutoImport""" """Constructor for GuiAutoImport"""
self.settings=settings self.settings=settings
self.config=config self.config=config
self.sql = sql
imp = self.config.get_import_parameters() imp = self.config.get_import_parameters()
@ -44,7 +45,7 @@ class GuiAutoImport (threading.Thread):
self.input_settings = {} self.input_settings = {}
self.pipe_to_hud = None self.pipe_to_hud = None
self.importer = fpdb_import.Importer(self,self.settings, self.config) self.importer = fpdb_import.Importer(self, self.settings, self.config, self.sql)
self.importer.setCallHud(True) self.importer.setCallHud(True)
self.importer.setMinPrint(settings['minPrint']) self.importer.setMinPrint(settings['minPrint'])
self.importer.setQuiet(False) self.importer.setQuiet(False)
@ -159,6 +160,7 @@ class GuiAutoImport (threading.Thread):
# - Ideally we want to release the lock if the auto-import is killed by some # - Ideally we want to release the lock if the auto-import is killed by some
# kind of exception - is this possible? # kind of exception - is this possible?
if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
try:
print "\nGlobal lock taken ..." print "\nGlobal lock taken ..."
self.doAutoImportBool = True self.doAutoImportBool = True
widget.set_label(u' _Stop Autoimport ') widget.set_label(u' _Stop Autoimport ')
@ -182,6 +184,9 @@ class GuiAutoImport (threading.Thread):
interval=int(self.intervalEntry.get_text()) interval=int(self.intervalEntry.get_text())
gobject.timeout_add(interval*1000, self.do_import) gobject.timeout_add(interval*1000, self.do_import)
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
else: else:
print "auto-import aborted - global lock not available" print "auto-import aborted - global lock not available"
else: # toggled off else: # toggled off

View File

@ -21,6 +21,7 @@ import os
import sys import sys
from time import time from time import time
from optparse import OptionParser from optparse import OptionParser
import traceback
# pyGTK modules # pyGTK modules
import pygtk import pygtk
@ -34,6 +35,9 @@ import Configuration
class GuiBulkImport(): class GuiBulkImport():
# CONFIGURATION - update these as preferred:
allowThreads = True # set to True to try out the threads field
# not used # not used
def import_dir(self): def import_dir(self):
"""imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it""" """imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it"""
@ -67,12 +71,19 @@ class GuiBulkImport():
self.importer.setHandsInDB(self.n_hands_in_db) self.importer.setHandsInDB(self.n_hands_in_db)
cb_model = self.cb_dropindexes.get_model() cb_model = self.cb_dropindexes.get_model()
cb_index = self.cb_dropindexes.get_active() cb_index = self.cb_dropindexes.get_active()
cb_hmodel = self.cb_drophudcache.get_model()
cb_hindex = self.cb_drophudcache.get_active()
self.lab_info.set_text("Importing") # doesn't display :-(
if cb_index: if cb_index:
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")
if cb_hindex:
self.importer.setDropHudCache(cb_hmodel[cb_hindex][0])
else:
self.importer.setDropHudCache("auto")
sitename = 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.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename) self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename)
self.importer.setCallHud(False) self.importer.setCallHud(False)
@ -81,14 +92,21 @@ class GuiBulkImport():
ttime = time() - starttime ttime = time() - starttime
if ttime == 0: if ttime == 0:
ttime = 1 ttime = 1
print 'GuiBulkImport.load done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\ print 'GuiBulkImport.load done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %.0f/sec'\
% (stored, dups, partial, errs, ttime, stored / ttime) % (stored, dups, partial, errs, ttime, (stored+0.0) / ttime)
self.importer.clearFileList() self.importer.clearFileList()
if self.n_hands_in_db == 0 and stored > 0:
self.cb_dropindexes.set_sensitive(True)
self.cb_dropindexes.set_active(0)
self.lab_drop.set_sensitive(True)
self.cb_drophudcache.set_sensitive(True)
self.cb_drophudcache.set_active(0)
self.lab_hdrop.set_sensitive(True)
self.lab_info.set_text("Import finished") self.lab_info.set_text("Import finished")
except: except:
print "bulkimport.loadclicked error: "+str(sys.exc_value) err = traceback.extract_tb(sys.exc_info()[2])[-1]
pass print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
self.settings['global_lock'].release() self.settings['global_lock'].release()
else: else:
print "bulk-import aborted - global lock not available" print "bulk-import aborted - global lock not available"
@ -111,7 +129,7 @@ class GuiBulkImport():
self.chooser.show() self.chooser.show()
# Table widget to hold the settings # Table widget to hold the settings
self.table = gtk.Table(rows = 3, columns = 5, homogeneous = False) self.table = gtk.Table(rows = 5, columns = 5, homogeneous = False)
self.vbox.add(self.table) self.vbox.add(self.table)
self.table.show() self.table.show()
@ -126,6 +144,7 @@ class GuiBulkImport():
self.table.attach(self.lab_status, 1, 2, 0, 1, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.lab_status, 1, 2, 0, 1, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_status.show() self.lab_status.show()
self.lab_status.set_justify(gtk.JUSTIFY_RIGHT) self.lab_status.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_status.set_alignment(1.0, 0.5)
# spin button - status # spin button - status
status_adj = gtk.Adjustment(value=100, lower=0, upper=300, step_incr=10, page_incr=1, page_size=0) #not sure what upper value should be! status_adj = gtk.Adjustment(value=100, lower=0, upper=300, step_incr=10, page_incr=1, page_size=0) #not sure what upper value should be!
@ -137,14 +156,17 @@ class GuiBulkImport():
self.lab_threads = gtk.Label("Number of threads:") self.lab_threads = gtk.Label("Number of threads:")
self.table.attach(self.lab_threads, 3, 4, 0, 1, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.lab_threads, 3, 4, 0, 1, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_threads.show() self.lab_threads.show()
if not self.allowThreads:
self.lab_threads.set_sensitive(False) self.lab_threads.set_sensitive(False)
self.lab_threads.set_justify(gtk.JUSTIFY_RIGHT) self.lab_threads.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_threads.set_alignment(1.0, 0.5)
# spin button - threads # spin button - threads
threads_adj = gtk.Adjustment(value=0, lower=0, upper=10, step_incr=1, page_incr=1, page_size=0) #not sure what upper value should be! threads_adj = gtk.Adjustment(value=0, lower=0, upper=32, step_incr=1, page_incr=1, page_size=0) #not sure what upper value should be!
self.spin_threads = gtk.SpinButton(adjustment=threads_adj, climb_rate=0.0, digits=0) self.spin_threads = gtk.SpinButton(adjustment=threads_adj, climb_rate=0.0, digits=0)
self.table.attach(self.spin_threads, 4, 5, 0, 1, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.spin_threads, 4, 5, 0, 1, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.spin_threads.show() self.spin_threads.show()
if not self.allowThreads:
self.spin_threads.set_sensitive(False) self.spin_threads.set_sensitive(False)
# checkbox - fail on error? # checkbox - fail on error?
@ -157,6 +179,7 @@ class GuiBulkImport():
self.table.attach(self.lab_hands, 1, 2, 1, 2, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.lab_hands, 1, 2, 1, 2, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_hands.show() self.lab_hands.show()
self.lab_hands.set_justify(gtk.JUSTIFY_RIGHT) self.lab_hands.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_hands.set_alignment(1.0, 0.5)
# spin button - hands to import # spin button - hands to import
hands_adj = gtk.Adjustment(value=0, lower=0, upper=10, step_incr=1, page_incr=1, page_size=0) #not sure what upper value should be! hands_adj = gtk.Adjustment(value=0, lower=0, upper=10, step_incr=1, page_incr=1, page_size=0) #not sure what upper value should be!
@ -169,6 +192,7 @@ class GuiBulkImport():
self.table.attach(self.lab_drop, 3, 4, 1, 2, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.lab_drop, 3, 4, 1, 2, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_drop.show() self.lab_drop.show()
self.lab_drop.set_justify(gtk.JUSTIFY_RIGHT) self.lab_drop.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_drop.set_alignment(1.0, 0.5)
# ComboBox - drop indexes # ComboBox - drop indexes
self.cb_dropindexes = gtk.combo_box_new_text() self.cb_dropindexes = gtk.combo_box_new_text()
@ -181,9 +205,10 @@ class GuiBulkImport():
# label - filter # label - filter
self.lab_filter = gtk.Label("Site filter:") self.lab_filter = gtk.Label("Site filter:")
self.table.attach(self.lab_filter, 2, 3, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.lab_filter, 1, 2, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_filter.show() self.lab_filter.show()
self.lab_filter.set_justify(gtk.JUSTIFY_RIGHT) self.lab_filter.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_filter.set_alignment(1.0, 0.5)
# ComboBox - filter # ComboBox - filter
self.cbfilter = gtk.combo_box_new_text() self.cbfilter = gtk.combo_box_new_text()
@ -191,21 +216,42 @@ class GuiBulkImport():
print w print w
self.cbfilter.append_text(w) self.cbfilter.append_text(w)
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, 2, 3, 2, 3, xpadding = 10, ypadding = 1, yoptions=gtk.SHRINK)
self.cbfilter.show() self.cbfilter.show()
# label - info # label - drop hudcache
self.lab_info = gtk.Label() self.lab_hdrop = gtk.Label("Drop HudCache:")
self.table.attach(self.lab_info, 0, 4, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.lab_hdrop, 3, 4, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_info.show() self.lab_hdrop.show()
self.lab_hdrop.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_hdrop.set_alignment(1.0, 0.5)
# ComboBox - drop hudcache
self.cb_drophudcache = gtk.combo_box_new_text()
self.cb_drophudcache.append_text('auto')
self.cb_drophudcache.append_text("don't drop")
self.cb_drophudcache.append_text('drop')
self.cb_drophudcache.set_active(0)
self.table.attach(self.cb_drophudcache, 4, 5, 2, 3, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.cb_drophudcache.show()
# button - Import # button - Import
self.load_button = gtk.Button('Import') # todo: rename variables to import too self.load_button = gtk.Button('Import') # todo: rename variables to import too
self.load_button.connect('clicked', self.load_clicked, self.load_button.connect('clicked', self.load_clicked,
'Import clicked') 'Import clicked')
self.table.attach(self.load_button, 4, 5, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.load_button, 2, 3, 4, 5, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.load_button.show() self.load_button.show()
# label - spacer (keeps rows 3 & 5 apart)
self.lab_spacer = gtk.Label()
self.table.attach(self.lab_spacer, 3, 5, 3, 4, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_spacer.show()
# label - info
self.lab_info = gtk.Label()
self.table.attach(self.lab_info, 3, 5, 4, 5, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_info.show()
# see how many hands are in the db and adjust accordingly # see how many hands are in the db and adjust accordingly
tcursor = self.importer.database.cursor tcursor = self.importer.database.cursor
tcursor.execute("Select count(1) from Hands") tcursor.execute("Select count(1) from Hands")
@ -217,6 +263,9 @@ class GuiBulkImport():
self.cb_dropindexes.set_active(2) self.cb_dropindexes.set_active(2)
self.cb_dropindexes.set_sensitive(False) self.cb_dropindexes.set_sensitive(False)
self.lab_drop.set_sensitive(False) self.lab_drop.set_sensitive(False)
self.cb_drophudcache.set_active(2)
self.cb_drophudcache.set_sensitive(False)
self.lab_hdrop.set_sensitive(False)
def main(argv=None): def main(argv=None):
"""main can also be called in the python interpreter, by supplying the command line as the argument.""" """main can also be called in the python interpreter, by supplying the command line as the argument."""

View File

@ -210,7 +210,7 @@
<game cols="3" db="fpdb" game_name="holdem" rows="2" aux="mucked"> <game cols="3" db="fpdb" game_name="holdem" rows="2" aux="mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
@ -219,7 +219,7 @@
<game cols="3" db="fpdb" game_name="razz" rows="2" aux="stud_mucked"> <game cols="3" db="fpdb" game_name="razz" rows="2" aux="stud_mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
@ -228,7 +228,7 @@
<game cols="3" db="fpdb" game_name="omahahi" rows="2" aux="mucked"> <game cols="3" db="fpdb" game_name="omahahi" rows="2" aux="mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
@ -237,7 +237,7 @@
<game cols="3" db="fpdb" game_name="omahahilo" rows="2" aux="mucked"> <game cols="3" db="fpdb" game_name="omahahilo" rows="2" aux="mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
@ -246,7 +246,7 @@
<game cols="3" db="fpdb" game_name="studhi" rows="2" aux="stud_mucked"> <game cols="3" db="fpdb" game_name="studhi" rows="2" aux="stud_mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
@ -255,7 +255,7 @@
<game cols="3" db="fpdb" game_name="studhilo" rows="2" aux="stud_mucked"> <game cols="3" db="fpdb" game_name="studhilo" rows="2" aux="stud_mucked">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq1" tip="tip1"> </stat>
<stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="1" stat_name="n" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
@ -274,18 +274,18 @@
<pu_stat pu_stat_name="wmsd"> </pu_stat> <pu_stat pu_stat_name="wmsd"> </pu_stat>
<pu_stat pu_stat_name="wtsd"> </pu_stat> <pu_stat pu_stat_name="wtsd"> </pu_stat>
<pu_stat pu_stat_name="WMsF"> </pu_stat> <pu_stat pu_stat_name="WMsF"> </pu_stat>
<pu_stat pu_stat_name="a_freq_1"> </pu_stat> <pu_stat pu_stat_name="a_freq1"> </pu_stat>
<pu_stat pu_stat_name="a_freq_2"> </pu_stat> <pu_stat pu_stat_name="a_freq2"> </pu_stat>
<pu_stat pu_stat_name="a_freq_3"> </pu_stat> <pu_stat pu_stat_name="a_freq3"> </pu_stat>
<pu_stat pu_stat_name="a_freq_4"> </pu_stat> <pu_stat pu_stat_name="a_freq4"> </pu_stat>
<pu_stat pu_stat_name="cb_1"> </pu_stat> <pu_stat pu_stat_name="cb1"> </pu_stat>
<pu_stat pu_stat_name="cb_2"> </pu_stat> <pu_stat pu_stat_name="cb2"> </pu_stat>
<pu_stat pu_stat_name="cb_3"> </pu_stat> <pu_stat pu_stat_name="cb3"> </pu_stat>
<pu_stat pu_stat_name="cb_4"> </pu_stat> <pu_stat pu_stat_name="cb4"> </pu_stat>
<pu_stat pu_stat_name="ffreq_1"> </pu_stat> <pu_stat pu_stat_name="ffreq1"> </pu_stat>
<pu_stat pu_stat_name="ffreq_2"> </pu_stat> <pu_stat pu_stat_name="ffreq2"> </pu_stat>
<pu_stat pu_stat_name="ffreq_3"> </pu_stat> <pu_stat pu_stat_name="ffreq3"> </pu_stat>
<pu_stat pu_stat_name="ffreq_4"> </pu_stat> <pu_stat pu_stat_name="ffreq4"> </pu_stat>
</pu> </pu>
</popup_windows> </popup_windows>

View File

@ -31,6 +31,7 @@ Main for FreePokerTools HUD.
import sys import sys
import os import os
import Options import Options
import traceback
(options, sys.argv) = Options.fpdb_options() (options, sys.argv) = Options.fpdb_options()
@ -55,7 +56,23 @@ import Database
import Tables import Tables
import Hud import Hud
aggregate_stats = {"ring": False, "tour": False} # config file! # To add to config:
aggregate_stats = {"ring": False, "tour": False} # uses agg_bb_mult
hud_style = 'A' # A=All-time
# S=Session
# T=timed (last n days - set hud_days to required value)
# Future values may also include:
# H=Hands (last n hands)
hud_days = 90 # Max number of days from each player to use for hud stats
agg_bb_mult = 100 # 1 = no aggregation. When aggregating stats across levels larger blinds
# must be < (agg_bb_mult * smaller blinds) to be aggregated
# ie. 100 will aggregate almost everything, 2 will probably agg just the
# next higher and lower levels into the current one, try 3/10/30/100
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)
#hud_hands = 0 # Max number of hands from each player to use for hud stats (not used)
class HUD_main(object): class HUD_main(object):
"""A main() object to own both the read_stdin thread and the gui.""" """A main() object to own both the read_stdin thread and the gui."""
@ -144,6 +161,7 @@ class HUD_main(object):
# need their own access to the database, but should open their own # need their own access to the database, but should open their own
# if it is required. # if it is required.
self.db_connection = Database.Database(self.config, self.db_name, 'temp') self.db_connection = Database.Database(self.config, self.db_name, 'temp')
self.db_connection.init_hud_stat_vars(hud_days)
tourny_finder = re.compile('(\d+) (\d+)') tourny_finder = re.compile('(\d+) (\d+)')
while 1: # wait for a new hand number on stdin while 1: # wait for a new hand number on stdin
@ -156,13 +174,17 @@ class HUD_main(object):
# if there is a db error, complain, skip hand, and proceed # if there is a db error, complain, skip hand, and proceed
try: try:
(table_name, max, poker_game, type) = self.db_connection.get_table_name(new_hand_id) (table_name, max, poker_game, type) = self.db_connection.get_table_name(new_hand_id)
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate = aggregate_stats[type]) stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate_stats[type]
,hud_style, agg_bb_mult)
cards = self.db_connection.get_cards(new_hand_id) cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id) comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud! if comm_cards != {}: # stud!
cards['common'] = comm_cards['common'] cards['common'] = comm_cards['common']
except Exception, err: except Exception, err:
print "db error: skipping ", new_hand_id, err err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "db error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
if new_hand_id: # new_hand_id is none if we had an error prior to the store
sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id))) sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
continue continue

View File

@ -40,10 +40,12 @@ class Hand(object):
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'} LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''} SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'} MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7}
def __init__(self, sitename, gametype, handText, builtFrom = "HHC"): def __init__(self, sitename, gametype, handText, builtFrom = "HHC"):
self.sitename = sitename self.sitename = sitename
self.siteId = self.SITEIDS[sitename]
self.stats = DerivedStats.DerivedStats(self) self.stats = DerivedStats.DerivedStats(self)
self.gametype = gametype self.gametype = gametype
self.starttime = 0 self.starttime = 0

View File

@ -204,7 +204,9 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
logging.info("Unsupported game type: %s" % gametype) logging.info("Unsupported game type: %s" % gametype)
if hand: if hand:
# uncomment these to calculate some stats
# print hand # print hand
# hand.stats.getStats(hand)
hand.writeHand(self.out_fh) hand.writeHand(self.out_fh)
return hand return hand
else: else:

View File

@ -32,6 +32,9 @@ def fpdb_options():
parser.add_option("-c", "--configFile", parser.add_option("-c", "--configFile",
dest="config", default=None, dest="config", default=None,
help="Specifies a configuration file.") help="Specifies a configuration file.")
parser.add_option("-r", "--rerunPython",
action="store_true",
help="Indicates program was restarted with a different path (only allowed once).")
(options, sys.argv) = parser.parse_args() (options, sys.argv) = parser.parse_args()
return (options, sys.argv) return (options, sys.argv)

View File

@ -29,9 +29,11 @@ class PokerStars(HandHistoryConverter):
############################################################ ############################################################
# Class Variables # Class Variables
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games
sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\x80", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE
substitutions = { substitutions = {
'LEGAL_ISO' : "USD|EUR|GBP|CAD", # legal ISO currency codes 'LEGAL_ISO' : "USD|EUR|GBP|CAD", # legal ISO currency codes
'LS' : "\$" # legal currency symbols 'LS' : "\$|\x80|\xa3" # legal currency symbols ADD Euro, Sterling, etc HERE
} }
# Static regexes # Static regexes
@ -48,25 +50,32 @@ class PokerStars(HandHistoryConverter):
(-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)? (-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)?
\(? # open paren of the stakes \(? # open paren of the stakes
(?P<CURRENCY>%(LS)s|)? (?P<CURRENCY>%(LS)s|)?
(?P<SB>[.0-9]+)/%(LS)s? (?P<SB>[.0-9]+)/(%(LS)s)?
(?P<BB>[.0-9]+) (?P<BB>[.0-9]+)
\s?(?P<ISO>%(LEGAL_ISO)s)? \s?(?P<ISO>%(LEGAL_ISO)s)?
\)\s-\s # close paren of the stakes \)\s-\s # close paren of the stakes
(?P<DATETIME>.*$)""" % substitutions, (?P<DATETIME>.*$)""" % substitutions,
re.MULTILINE|re.VERBOSE) re.MULTILINE|re.VERBOSE)
re_SplitHands = re.compile('\n\n+')
re_TailSplitHands = re.compile('(\n\n\n+)') re_PlayerInfo = re.compile("""
re_HandInfo = re.compile("""^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s ^Seat\s(?P<SEAT>[0-9]+):\s
(?P<PNAME>.*)\s
\((%(LS)s)?(?P<CASH>[.0-9]+)\sin\schips\)""" % substitutions,
re.MULTILINE|re.VERBOSE)
re_HandInfo = re.compile("""
^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s
((?P<MAX>\d+)-max\s)? ((?P<MAX>\d+)-max\s)?
(?P<PLAY>\(Play\sMoney\)\s)? (?P<PLAY>\(Play\sMoney\)\s)?
(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""", (Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
re.MULTILINE|re.VERBOSE) re.MULTILINE|re.VERBOSE)
re_SplitHands = re.compile('\n\n+')
re_TailSplitHands = re.compile('(\n\n\n+)')
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_Board = re.compile(r"\[(?P<CARDS>.+)\]") 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]+)') # 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]+)')
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'}
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\ """\
@ -91,19 +100,21 @@ follow : whether to tail -f the input"""
# They still identify the hero. # They still identify the hero.
self.compiledPlayers = players self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")" player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
subst = {'PLYR': player_re, 'CUR': self.sym[hand.gametype['currency']]}
logging.debug("player_re: " + player_re) 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_PostSB = re.compile(r"^%(PLYR)s: posts small blind %(CUR)s(?P<SB>[.0-9]+)" % subst, re.MULTILINE)
self.re_PostBB = re.compile(r"^%s: posts big blind \$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE) self.re_PostBB = re.compile(r"^%(PLYR)s: posts big blind %(CUR)s(?P<BB>[.0-9]+)" % subst, re.MULTILINE)
self.re_Antes = re.compile(r"^%s: posts the ante \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE) self.re_Antes = re.compile(r"^%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[.0-9]+)" % subst, re.MULTILINE)
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE) self.re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst, re.MULTILINE)
self.re_PostBoth = re.compile(r"^%s: posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE) self.re_PostBoth = re.compile(r"^%(PLYR)s: posts small \& big blinds \[%(CUR)s (?P<SBBB>[.0-9]+)" % subst, re.MULTILINE)
self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE) self.re_HeroCards = re.compile(r"^Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE)
self.re_Action = re.compile(r"""^%s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat) self.re_Action = re.compile(r"""
(\s\$?(?P<BET>[.\d]+))?(\sto\s\$?(?P<BETTO>[.\d]+))? # the number discarded goes in <BET> ^%(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)
(\s%(CUR)s(?P<BET>[.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[.\d]+))? # the number discarded goes in <BET>
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?""" (\scards?(\s\[(?P<DISCARDED>.+?)\])?)?"""
% player_re, re.MULTILINE|re.VERBOSE) % subst, re.MULTILINE|re.VERBOSE)
self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P<CARDS>.*)\]" % 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_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(%(CUR)s(?P<POT>[.\d]+)\)(, mucked| with.*|)" % subst, re.MULTILINE)
self.re_sitsOut = re.compile("^%s sits out" % 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 (\(.*\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE) self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)

View File

@ -19,9 +19,15 @@ Set up all of the SQL statements for a given game and database type.
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# NOTES: The sql statements use the placeholder %s for bind variables
# which is then replaced by ? for sqlite. Comments can be included
# within sql statements using C style /* ... */ comments, BUT
# THE COMMENTS MUST NOT INCLUDE %s OR ?.
######################################################################## ########################################################################
# Standard Library modules # Standard Library modules
import re
# pyGTK modules # pyGTK modules
@ -165,12 +171,11 @@ class Sql:
################################ ################################
# List tables # List tables
################################ ################################
print "db_server =", db_server
if db_server == 'mysql': if db_server == 'mysql':
self.query['list_tables'] = """SHOW TABLES""" self.query['list_tables'] = """SHOW TABLES"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['list_tables'] = """SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'""" self.query['list_tables'] = """SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['list_tables'] = """SELECT name FROM sqlite_master self.query['list_tables'] = """SELECT name FROM sqlite_master
WHERE type='table' WHERE type='table'
ORDER BY name;""" ORDER BY name;"""
@ -189,12 +194,12 @@ class Sql:
self.query['createSettingsTable'] = """CREATE TABLE Settings ( self.query['createSettingsTable'] = """CREATE TABLE Settings (
version SMALLINT NOT NULL) version SMALLINT NOT NULL)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createSettingsTable'] = """CREATE TABLE Settings (version SMALLINT)""" self.query['createSettingsTable'] = """CREATE TABLE Settings (version SMALLINT NOT NULL)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createSettingsTable'] = """CREATE TABLE Settings self.query['createSettingsTable'] = """CREATE TABLE Settings
(version INTEGER) """ (version INTEGER NOT NULL) """
################################ ################################
@ -207,12 +212,12 @@ class Sql:
name varchar(32) NOT NULL, name varchar(32) NOT NULL,
currency char(3) NOT NULL) currency char(3) NOT NULL)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createSitesTable'] = """CREATE TABLE Sites ( self.query['createSitesTable'] = """CREATE TABLE Sites (
id SERIAL, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
name varchar(32), name varchar(32),
currency char(3))""" currency char(3))"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createSitesTable'] = """CREATE TABLE Sites ( self.query['createSitesTable'] = """CREATE TABLE Sites (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
@ -237,7 +242,7 @@ class Sql:
smallBet int NOT NULL, smallBet int NOT NULL,
bigBet int NOT NULL) bigBet int NOT NULL)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createGametypesTable'] = """CREATE TABLE Gametypes ( self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
id SERIAL, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
@ -250,7 +255,7 @@ class Sql:
bigBlind int, bigBlind int,
smallBet int, smallBet int,
bigBet int)""" bigBet int)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createGametypesTable'] = """CREATE TABLE GameTypes ( self.query['createGametypesTable'] = """CREATE TABLE GameTypes (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
siteId INTEGER, siteId INTEGER,
@ -278,20 +283,20 @@ class Sql:
comment text, comment text,
commentTs DATETIME) commentTs DATETIME)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createPlayersTable'] = """CREATE TABLE Players ( self.query['createPlayersTable'] = """CREATE TABLE Players (
id SERIAL, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
name VARCHAR(32), name VARCHAR(32),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text, comment text,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createPlayersTable'] = """CREATE TABLE Players ( self.query['createPlayersTable'] = """CREATE TABLE Players (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
name TEXT, name TEXT,
siteId INTEGER, siteId INTEGER,
comment TEXT, comment TEXT,
commentTs BLOB, commentTs REAL,
FOREIGN KEY(siteId) REFERENCES Sites(id) ON DELETE CASCADE)""" FOREIGN KEY(siteId) REFERENCES Sites(id) ON DELETE CASCADE)"""
@ -309,7 +314,7 @@ class Sql:
ratingTime DATETIME NOT NULL, ratingTime DATETIME NOT NULL,
handCount int NOT NULL) handCount int NOT NULL)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createAutoratesTable'] = """CREATE TABLE Autorates ( self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
id BIGSERIAL, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
@ -318,8 +323,15 @@ class Sql:
shortDesc char(8), shortDesc char(8),
ratingTime timestamp without time zone, ratingTime timestamp without time zone,
handCount int)""" handCount int)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createAutoratesTable'] = """ """ self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
id INTEGER PRIMARY KEY,
playerId INT,
gametypeId INT,
description TEXT,
shortDesc TEXT,
ratingTime REAL,
handCount int)"""
################################ ################################
@ -361,7 +373,7 @@ class Sql:
comment TEXT, comment TEXT,
commentTs DATETIME) commentTs DATETIME)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createHandsTable'] = """CREATE TABLE Hands ( self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGSERIAL, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
tableName VARCHAR(20) NOT NULL, tableName VARCHAR(20) NOT NULL,
@ -395,18 +407,18 @@ class Sql:
showdownPot INT, /* pot size at sd/street7 */ showdownPot INT, /* pot size at sd/street7 */
comment TEXT, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createHandsTable'] = """CREATE TABLE Hands ( self.query['createHandsTable'] = """CREATE TABLE Hands (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
tableName TEXT(20), tableName TEXT(20),
siteHandNo INTEGER, siteHandNo INTEGER,
gametypeId INTEGER, gametypeId INTEGER,
handStart BLOB, handStart REAL,
importTime BLOB, importTime REAL,
seats INTEGER, seats INTEGER,
maxSeats INTEGER, maxSeats INTEGER,
comment TEXT, comment TEXT,
commentTs BLOB, commentTs REAL,
FOREIGN KEY(gametypeId) REFERENCES Gametypes(id) ON DELETE CASCADE)""" FOREIGN KEY(gametypeId) REFERENCES Gametypes(id) ON DELETE CASCADE)"""
@ -423,17 +435,22 @@ class Sql:
knockout INT NOT NULL, knockout INT NOT NULL,
rebuyOrAddon BOOLEAN NOT NULL) rebuyOrAddon BOOLEAN NOT NULL)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes ( self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
id SERIAL, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
siteId INT, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId INT NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
buyin INT, buyin INT NOT NULL,
fee INT, fee INT NOT NULL,
knockout INT, knockout INT NOT NULL,
rebuyOrAddon BOOLEAN)""" rebuyOrAddon BOOLEAN NOT NULL)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createTourneyTypesTable'] = """ """ self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
id INTEGER PRIMARY KEY,
siteId INT NOT NULL,
buyin INT NOT NULL,
fee INT NOT NULL,
knockout INT NOT NULL,
rebuyOrAddon BOOLEAN NOT NULL)"""
################################ ################################
# Create Tourneys # Create Tourneys
@ -450,7 +467,7 @@ class Sql:
comment TEXT, comment TEXT,
commentTs DATETIME) commentTs DATETIME)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createTourneysTable'] = """CREATE TABLE Tourneys ( self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
id SERIAL, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
@ -460,16 +477,16 @@ class Sql:
startTime timestamp without time zone, startTime timestamp without time zone,
comment TEXT, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createTourneysTable'] = """CREATE TABLE TourneyTypes ( self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
siteId INTEGER, tourneyTypeId INT,
buyin INTEGER, siteTourneyNo INT,
fee INTEGER, entries INT,
knockout INTEGER, prizepool INT,
rebuyOrAddon BOOL, startTime REAL,
FOREIGN KEY(siteId) REFERENCES Sites(id) ON DELETE CASCADE)""" comment TEXT,
commentTs REAL)"""
################################ ################################
# Create HandsPlayers # Create HandsPlayers
################################ ################################
@ -495,82 +512,82 @@ class Sql:
ante INT, ante INT,
winnings int NOT NULL, winnings int NOT NULL,
rake int NOT NULL, rake int NOT NULL,
totalProfit INT NOT NULL, totalProfit INT,
comment text, comment text,
commentTs DATETIME, commentTs DATETIME,
tourneysPlayersId BIGINT UNSIGNED, tourneysPlayersId BIGINT UNSIGNED,
tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
wonWhenSeenStreet1 FLOAT NOT NULL, wonWhenSeenStreet1 FLOAT,
wonWhenSeenStreet2 FLOAT, wonWhenSeenStreet2 FLOAT,
wonWhenSeenStreet3 FLOAT, wonWhenSeenStreet3 FLOAT,
wonWhenSeenStreet4 FLOAT, wonWhenSeenStreet4 FLOAT,
wonAtSD FLOAT NOT NULL, wonAtSD FLOAT,
street0VPI BOOLEAN NOT NULL, street0VPI BOOLEAN,
street0Aggr BOOLEAN NOT NULL, street0Aggr BOOLEAN,
street0_3BChance BOOLEAN NOT NULL, street0_3BChance BOOLEAN,
street0_3BDone BOOLEAN NOT NULL, street0_3BDone BOOLEAN,
street0_4BChance BOOLEAN, street0_4BChance BOOLEAN,
street0_4BDone BOOLEAN, street0_4BDone BOOLEAN,
other3BStreet0 BOOLEAN, other3BStreet0 BOOLEAN,
other4BStreet0 BOOLEAN, other4BStreet0 BOOLEAN,
street1Seen BOOLEAN NOT NULL, street1Seen BOOLEAN,
street2Seen BOOLEAN NOT NULL, street2Seen BOOLEAN,
street3Seen BOOLEAN NOT NULL, street3Seen BOOLEAN,
street4Seen BOOLEAN NOT NULL, street4Seen BOOLEAN,
sawShowdown BOOLEAN NOT NULL, sawShowdown BOOLEAN,
street1Aggr BOOLEAN NOT NULL, street1Aggr BOOLEAN,
street2Aggr BOOLEAN NOT NULL, street2Aggr BOOLEAN,
street3Aggr BOOLEAN NOT NULL, street3Aggr BOOLEAN,
street4Aggr BOOLEAN NOT NULL, street4Aggr BOOLEAN,
otherRaisedStreet0 BOOLEAN, otherRaisedStreet0 BOOLEAN,
otherRaisedStreet1 BOOLEAN NOT NULL, otherRaisedStreet1 BOOLEAN,
otherRaisedStreet2 BOOLEAN NOT NULL, otherRaisedStreet2 BOOLEAN,
otherRaisedStreet3 BOOLEAN NOT NULL, otherRaisedStreet3 BOOLEAN,
otherRaisedStreet4 BOOLEAN NOT NULL, otherRaisedStreet4 BOOLEAN,
foldToOtherRaisedStreet0 BOOLEAN, foldToOtherRaisedStreet0 BOOLEAN,
foldToOtherRaisedStreet1 BOOLEAN NOT NULL, foldToOtherRaisedStreet1 BOOLEAN,
foldToOtherRaisedStreet2 BOOLEAN NOT NULL, foldToOtherRaisedStreet2 BOOLEAN,
foldToOtherRaisedStreet3 BOOLEAN NOT NULL, foldToOtherRaisedStreet3 BOOLEAN,
foldToOtherRaisedStreet4 BOOLEAN NOT NULL, foldToOtherRaisedStreet4 BOOLEAN,
stealAttemptChance BOOLEAN NOT NULL, stealAttemptChance BOOLEAN,
stealAttempted BOOLEAN NOT NULL, stealAttempted BOOLEAN,
foldBbToStealChance BOOLEAN NOT NULL, foldBbToStealChance BOOLEAN,
foldedBbToSteal BOOLEAN NOT NULL, foldedBbToSteal BOOLEAN,
foldSbToStealChance BOOLEAN NOT NULL, foldSbToStealChance BOOLEAN,
foldedSbToSteal BOOLEAN NOT NULL, foldedSbToSteal BOOLEAN,
street1CBChance BOOLEAN NOT NULL, street1CBChance BOOLEAN,
street1CBDone BOOLEAN NOT NULL, street1CBDone BOOLEAN,
street2CBChance BOOLEAN NOT NULL, street2CBChance BOOLEAN,
street2CBDone BOOLEAN NOT NULL, street2CBDone BOOLEAN,
street3CBChance BOOLEAN NOT NULL, street3CBChance BOOLEAN,
street3CBDone BOOLEAN NOT NULL, street3CBDone BOOLEAN,
street4CBChance BOOLEAN NOT NULL, street4CBChance BOOLEAN,
street4CBDone BOOLEAN NOT NULL, street4CBDone BOOLEAN,
foldToStreet1CBChance BOOLEAN NOT NULL, foldToStreet1CBChance BOOLEAN,
foldToStreet1CBDone BOOLEAN NOT NULL, foldToStreet1CBDone BOOLEAN,
foldToStreet2CBChance BOOLEAN NOT NULL, foldToStreet2CBChance BOOLEAN,
foldToStreet2CBDone BOOLEAN NOT NULL, foldToStreet2CBDone BOOLEAN,
foldToStreet3CBChance BOOLEAN NOT NULL, foldToStreet3CBChance BOOLEAN,
foldToStreet3CBDone BOOLEAN NOT NULL, foldToStreet3CBDone BOOLEAN,
foldToStreet4CBChance BOOLEAN NOT NULL, foldToStreet4CBChance BOOLEAN,
foldToStreet4CBDone BOOLEAN NOT NULL, foldToStreet4CBDone BOOLEAN,
street1CheckCallRaiseChance BOOLEAN NOT NULL, street1CheckCallRaiseChance BOOLEAN,
street1CheckCallRaiseDone BOOLEAN NOT NULL, street1CheckCallRaiseDone BOOLEAN,
street2CheckCallRaiseChance BOOLEAN NOT NULL, street2CheckCallRaiseChance BOOLEAN,
street2CheckCallRaiseDone BOOLEAN NOT NULL, street2CheckCallRaiseDone BOOLEAN,
street3CheckCallRaiseChance BOOLEAN NOT NULL, street3CheckCallRaiseChance BOOLEAN,
street3CheckCallRaiseDone BOOLEAN NOT NULL, street3CheckCallRaiseDone BOOLEAN,
street4CheckCallRaiseChance BOOLEAN NOT NULL, street4CheckCallRaiseChance BOOLEAN,
street4CheckCallRaiseDone BOOLEAN NOT NULL, street4CheckCallRaiseDone BOOLEAN,
street0Calls TINYINT, street0Calls TINYINT,
street1Calls TINYINT, street1Calls TINYINT,
@ -592,7 +609,7 @@ class Sql:
FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id)) FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id))
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers ( self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
id BIGSERIAL, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
handId BIGINT NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id), handId BIGINT NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
@ -613,82 +630,82 @@ class Sql:
ante INT, ante INT,
winnings int NOT NULL, winnings int NOT NULL,
rake int NOT NULL, rake int NOT NULL,
totalProfit INT NOT NULL, totalProfit INT,
comment text, comment text,
commentTs timestamp without time zone, commentTs timestamp without time zone,
tourneysPlayersId BIGINT, tourneysPlayersId BIGINT,
tourneyTypeId INT NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), tourneyTypeId INT NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
wonWhenSeenStreet1 FLOAT NOT NULL, wonWhenSeenStreet1 FLOAT,
wonWhenSeenStreet2 FLOAT, wonWhenSeenStreet2 FLOAT,
wonWhenSeenStreet3 FLOAT, wonWhenSeenStreet3 FLOAT,
wonWhenSeenStreet4 FLOAT, wonWhenSeenStreet4 FLOAT,
wonAtSD FLOAT NOT NULL, wonAtSD FLOAT,
street0VPI BOOLEAN NOT NULL, street0VPI BOOLEAN,
street0Aggr BOOLEAN NOT NULL, street0Aggr BOOLEAN,
street0_3BChance BOOLEAN NOT NULL, street0_3BChance BOOLEAN,
street0_3BDone BOOLEAN NOT NULL, street0_3BDone BOOLEAN,
street0_4BChance BOOLEAN, street0_4BChance BOOLEAN,
street0_4BDone BOOLEAN, street0_4BDone BOOLEAN,
other3BStreet0 BOOLEAN, other3BStreet0 BOOLEAN,
other4BStreet0 BOOLEAN, other4BStreet0 BOOLEAN,
street1Seen BOOLEAN NOT NULL, street1Seen BOOLEAN,
street2Seen BOOLEAN NOT NULL, street2Seen BOOLEAN,
street3Seen BOOLEAN NOT NULL, street3Seen BOOLEAN,
street4Seen BOOLEAN NOT NULL, street4Seen BOOLEAN,
sawShowdown BOOLEAN NOT NULL, sawShowdown BOOLEAN,
street1Aggr BOOLEAN NOT NULL, street1Aggr BOOLEAN,
street2Aggr BOOLEAN NOT NULL, street2Aggr BOOLEAN,
street3Aggr BOOLEAN NOT NULL, street3Aggr BOOLEAN,
street4Aggr BOOLEAN NOT NULL, street4Aggr BOOLEAN,
otherRaisedStreet0 BOOLEAN, otherRaisedStreet0 BOOLEAN,
otherRaisedStreet1 BOOLEAN NOT NULL, otherRaisedStreet1 BOOLEAN,
otherRaisedStreet2 BOOLEAN NOT NULL, otherRaisedStreet2 BOOLEAN,
otherRaisedStreet3 BOOLEAN NOT NULL, otherRaisedStreet3 BOOLEAN,
otherRaisedStreet4 BOOLEAN NOT NULL, otherRaisedStreet4 BOOLEAN,
foldToOtherRaisedStreet0 BOOLEAN, foldToOtherRaisedStreet0 BOOLEAN,
foldToOtherRaisedStreet1 BOOLEAN NOT NULL, foldToOtherRaisedStreet1 BOOLEAN,
foldToOtherRaisedStreet2 BOOLEAN NOT NULL, foldToOtherRaisedStreet2 BOOLEAN,
foldToOtherRaisedStreet3 BOOLEAN NOT NULL, foldToOtherRaisedStreet3 BOOLEAN,
foldToOtherRaisedStreet4 BOOLEAN NOT NULL, foldToOtherRaisedStreet4 BOOLEAN,
stealAttemptChance BOOLEAN NOT NULL, stealAttemptChance BOOLEAN,
stealAttempted BOOLEAN NOT NULL, stealAttempted BOOLEAN,
foldBbToStealChance BOOLEAN NOT NULL, foldBbToStealChance BOOLEAN,
foldedBbToSteal BOOLEAN NOT NULL, foldedBbToSteal BOOLEAN,
foldSbToStealChance BOOLEAN NOT NULL, foldSbToStealChance BOOLEAN,
foldedSbToSteal BOOLEAN NOT NULL, foldedSbToSteal BOOLEAN,
street1CBChance BOOLEAN NOT NULL, street1CBChance BOOLEAN,
street1CBDone BOOLEAN NOT NULL, street1CBDone BOOLEAN,
street2CBChance BOOLEAN NOT NULL, street2CBChance BOOLEAN,
street2CBDone BOOLEAN NOT NULL, street2CBDone BOOLEAN,
street3CBChance BOOLEAN NOT NULL, street3CBChance BOOLEAN,
street3CBDone BOOLEAN NOT NULL, street3CBDone BOOLEAN,
street4CBChance BOOLEAN NOT NULL, street4CBChance BOOLEAN,
street4CBDone BOOLEAN NOT NULL, street4CBDone BOOLEAN,
foldToStreet1CBChance BOOLEAN NOT NULL, foldToStreet1CBChance BOOLEAN,
foldToStreet1CBDone BOOLEAN NOT NULL, foldToStreet1CBDone BOOLEAN,
foldToStreet2CBChance BOOLEAN NOT NULL, foldToStreet2CBChance BOOLEAN,
foldToStreet2CBDone BOOLEAN NOT NULL, foldToStreet2CBDone BOOLEAN,
foldToStreet3CBChance BOOLEAN NOT NULL, foldToStreet3CBChance BOOLEAN,
foldToStreet3CBDone BOOLEAN NOT NULL, foldToStreet3CBDone BOOLEAN,
foldToStreet4CBChance BOOLEAN NOT NULL, foldToStreet4CBChance BOOLEAN,
foldToStreet4CBDone BOOLEAN NOT NULL, foldToStreet4CBDone BOOLEAN,
street1CheckCallRaiseChance BOOLEAN NOT NULL, street1CheckCallRaiseChance BOOLEAN,
street1CheckCallRaiseDone BOOLEAN NOT NULL, street1CheckCallRaiseDone BOOLEAN,
street2CheckCallRaiseChance BOOLEAN NOT NULL, street2CheckCallRaiseChance BOOLEAN,
street2CheckCallRaiseDone BOOLEAN NOT NULL, street2CheckCallRaiseDone BOOLEAN,
street3CheckCallRaiseChance BOOLEAN NOT NULL, street3CheckCallRaiseChance BOOLEAN,
street3CheckCallRaiseDone BOOLEAN NOT NULL, street3CheckCallRaiseDone BOOLEAN,
street4CheckCallRaiseChance BOOLEAN NOT NULL, street4CheckCallRaiseChance BOOLEAN,
street4CheckCallRaiseDone BOOLEAN NOT NULL, street4CheckCallRaiseDone BOOLEAN,
street0Calls SMALLINT, street0Calls SMALLINT,
street1Calls SMALLINT, street1Calls SMALLINT,
@ -709,8 +726,122 @@ class Sql:
actionString VARCHAR(15), actionString VARCHAR(15),
FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id))""" FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id))"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createHandsPlayersTable'] = """ """ self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
id INTEGER PRIMARY KEY,
handId INT NOT NULL,
playerId INT NOT NULL,
startCash INT NOT NULL,
position TEXT,
seatNo INT NOT NULL,
card1 INT NOT NULL, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */
card2 INT NOT NULL,
card3 INT,
card4 INT,
card5 INT,
card6 INT,
card7 INT,
startCards INT,
ante INT,
winnings INT NOT NULL,
rake INT NOT NULL,
totalProfit INT,
comment TEXT,
commentTs REAL,
tourneysPlayersId INT,
tourneyTypeId INT NOT NULL,
wonWhenSeenStreet1 REAL,
wonWhenSeenStreet2 REAL,
wonWhenSeenStreet3 REAL,
wonWhenSeenStreet4 REAL,
wonAtSD REAL,
street0VPI INT,
street0Aggr INT,
street0_3BChance INT,
street0_3BDone INT,
street0_4BChance INT,
street0_4BDone INT,
other3BStreet0 INT,
other4BStreet0 INT,
street1Seen INT,
street2Seen INT,
street3Seen INT,
street4Seen INT,
sawShowdown INT,
street1Aggr INT,
street2Aggr INT,
street3Aggr INT,
street4Aggr INT,
otherRaisedStreet0 INT,
otherRaisedStreet1 INT,
otherRaisedStreet2 INT,
otherRaisedStreet3 INT,
otherRaisedStreet4 INT,
foldToOtherRaisedStreet0 INT,
foldToOtherRaisedStreet1 INT,
foldToOtherRaisedStreet2 INT,
foldToOtherRaisedStreet3 INT,
foldToOtherRaisedStreet4 INT,
stealAttemptChance INT,
stealAttempted INT,
foldBbToStealChance INT,
foldedBbToSteal INT,
foldSbToStealChance INT,
foldedSbToSteal INT,
street1CBChance INT,
street1CBDone INT,
street2CBChance INT,
street2CBDone INT,
street3CBChance INT,
street3CBDone INT,
street4CBChance INT,
street4CBDone INT,
foldToStreet1CBChance INT,
foldToStreet1CBDone INT,
foldToStreet2CBChance INT,
foldToStreet2CBDone INT,
foldToStreet3CBChance INT,
foldToStreet3CBDone INT,
foldToStreet4CBChance INT,
foldToStreet4CBDone INT,
street1CheckCallRaiseChance INT,
street1CheckCallRaiseDone INT,
street2CheckCallRaiseChance INT,
street2CheckCallRaiseDone INT,
street3CheckCallRaiseChance INT,
street3CheckCallRaiseDone INT,
street4CheckCallRaiseChance INT,
street4CheckCallRaiseDone INT,
street0Calls INT,
street1Calls INT,
street2Calls INT,
street3Calls INT,
street4Calls INT,
street0Bets INT,
street1Bets INT,
street2Bets INT,
street3Bets INT,
street4Bets INT,
street0Raises INT,
street1Raises INT,
street2Raises INT,
street3Raises INT,
street4Raises INT,
actionString REAL)
"""
################################ ################################
@ -728,7 +859,7 @@ class Sql:
comment TEXT, comment TEXT,
commentTs DATETIME) commentTs DATETIME)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers ( self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
id BIGSERIAL, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id), tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
@ -738,7 +869,7 @@ class Sql:
winnings INT, winnings INT,
comment TEXT, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createTourneysPlayersTable'] = """ """ self.query['createTourneysPlayersTable'] = """ """
@ -758,7 +889,7 @@ class Sql:
comment TEXT, comment TEXT,
commentTs DATETIME) commentTs DATETIME)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions ( self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
id BIGSERIAL, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
handsPlayerId BIGINT, FOREIGN KEY (handsPlayerId) REFERENCES HandsPlayers(id), handsPlayerId BIGINT, FOREIGN KEY (handsPlayerId) REFERENCES HandsPlayers(id),
@ -769,7 +900,7 @@ class Sql:
amount INT, amount INT,
comment TEXT, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createHandsActionsTable'] = """ """ self.query['createHandsActionsTable'] = """ """
@ -878,7 +1009,7 @@ class Sql:
street4Raises INT) street4Raises INT)
ENGINE=INNODB""" ENGINE=INNODB"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['createHudCacheTable'] = """CREATE TABLE HudCache ( self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
id BIGSERIAL, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -977,37 +1108,136 @@ class Sql:
street3Raises INT, street3Raises INT,
street4Raises INT) street4Raises INT)
""" """
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['createHudCacheTable'] = """ """ self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
id INTEGER PRIMARY KEY,
gametypeId INT,
playerId INT,
activeSeats INT,
position TEXT,
tourneyTypeId INT,
styleKey TEXT NOT NULL, /* 1st char is style (A/T/H/S), other 6 are the key */
HDs INT,
wonWhenSeenStreet1 REAL NOT NULL,
wonWhenSeenStreet2 REAL,
wonWhenSeenStreet3 REAL,
wonWhenSeenStreet4 REAL,
wonAtSD REAL NOT NULL,
street0VPI INT NOT NULL,
street0Aggr INT,
street0_3BChance INT NOT NULL,
street0_3BDone INT NOT NULL,
street0_4BChance INT,
street0_4BDone INT,
other3BStreet0 INT,
other4BStreet0 INT,
street1Seen INT,
street2Seen INT,
street3Seen INT,
street4Seen INT,
sawShowdown INT,
street1Aggr INT,
street2Aggr INT,
street3Aggr INT,
street4Aggr INT,
otherRaisedStreet0 INT,
otherRaisedStreet1 INT,
otherRaisedStreet2 INT,
otherRaisedStreet3 INT,
otherRaisedStreet4 INT,
foldToOtherRaisedStreet0 INT,
foldToOtherRaisedStreet1 INT,
foldToOtherRaisedStreet2 INT,
foldToOtherRaisedStreet3 INT,
foldToOtherRaisedStreet4 INT,
stealAttemptChance INT,
stealAttempted INT,
foldBbToStealChance INT,
foldedBbToSteal INT,
foldSbToStealChance INT,
foldedSbToSteal INT,
street1CBChance INT,
street1CBDone INT,
street2CBChance INT,
street2CBDone INT,
street3CBChance INT,
street3CBDone INT,
street4CBChance INT,
street4CBDone INT,
foldToStreet1CBChance INT,
foldToStreet1CBDone INT,
foldToStreet2CBChance INT,
foldToStreet2CBDone INT,
foldToStreet3CBChance INT,
foldToStreet3CBDone INT,
foldToStreet4CBChance INT,
foldToStreet4CBDone INT,
totalProfit INT,
street1CheckCallRaiseChance INT,
street1CheckCallRaiseDone INT,
street2CheckCallRaiseChance INT,
street2CheckCallRaiseDone INT,
street3CheckCallRaiseChance INT,
street3CheckCallRaiseDone INT,
street4CheckCallRaiseChance INT,
street4CheckCallRaiseDone INT,
street0Calls INT,
street1Calls INT,
street2Calls INT,
street3Calls INT,
street4Calls INT,
street0Bets INT,
street1Bets INT,
street2Bets INT,
street3Bets INT,
street4Bets INT,
street0Raises INT,
street1Raises INT,
street2Raises INT,
street3Raises INT,
street4Raises INT)
"""
if db_server == 'mysql': if db_server == 'mysql':
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)""" self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)""" self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['addHandsIndex'] = """ """ self.query['addHandsIndex'] = """ """
if db_server == 'mysql': if db_server == 'mysql':
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)""" self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)""" self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['addHandsIndex'] = """ """ self.query['addHandsIndex'] = """ """
if db_server == 'mysql': if db_server == 'mysql':
self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)""" self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)"""
elif db_server == 'postgresql': # what is the correct value here? elif db_server == 'postgresql':
self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)""" self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)"""
elif db_server == 'sqlite': # what is the correct value here? elif db_server == 'sqlite':
self.query['addPlayersIndex'] = """ """ self.query['addPlayersIndex'] = """ """
self.query['get_last_hand'] = "select max(id) from Hands" self.query['get_last_hand'] = "select max(id) from Hands"
self.query['get_player_id'] = """ self.query['get_player_id'] = """
select Players.id AS player_id from Players, Sites select Players.id AS player_id
where Players.name = %(player)s from Players, Sites
and Sites.name = %(site)s where Players.name = %s
and Sites.name = %s
and Players.SiteId = Sites.id and Players.SiteId = Sites.id
""" """
@ -1074,7 +1304,7 @@ class Sql:
sum(hc.street4CheckCallRaiseChance) AS ccr_opp_4, sum(hc.street4CheckCallRaiseChance) AS ccr_opp_4,
sum(hc.street4CheckCallRaiseDone) AS ccr_4 sum(hc.street4CheckCallRaiseDone) AS ccr_4
FROM Hands h FROM Hands h
INNER JOIN HandsPlayers hp ON (hp.handId = %s) INNER JOIN HandsPlayers hp ON (hp.handId = h.id)
INNER JOIN HudCache hc ON ( hc.PlayerId = hp.PlayerId+0 INNER JOIN HudCache hc ON ( hc.PlayerId = hp.PlayerId+0
AND hc.gametypeId+0 = h.gametypeId+0) AND hc.gametypeId+0 = h.gametypeId+0)
INNER JOIN Players p ON (p.id = hp.PlayerId+0) INNER JOIN Players p ON (p.id = hp.PlayerId+0)
@ -1082,7 +1312,7 @@ class Sql:
AND hc.styleKey > %s AND hc.styleKey > %s
/* styleKey is currently 'd' (for date) followed by a yyyymmdd /* styleKey is currently 'd' (for date) followed by a yyyymmdd
date key. Set it to 0000000 or similar to get all records */ 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+ ?? /* also check activeseats here even if only 3 groups eg 2-3/4-6/7+
e.g. could use a multiplier: e.g. could use a multiplier:
AND h.seats > X / 1.25 and hp.seats < X * 1.25 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 where X is the number of active players at the current table (and
@ -1156,27 +1386,31 @@ class Sql:
sum(hc.street4CheckCallRaiseChance) AS ccr_opp_4, sum(hc.street4CheckCallRaiseChance) AS ccr_opp_4,
sum(hc.street4CheckCallRaiseDone) AS ccr_4 sum(hc.street4CheckCallRaiseDone) AS ccr_4
FROM Hands h FROM Hands h
INNER JOIN HandsPlayers hp ON (hp.handId = %s) INNER JOIN HandsPlayers hp ON (hp.handId = h.id)
INNER JOIN HudCache hc ON (hc.playerId = hp.playerId) INNER JOIN HudCache hc ON (hc.playerId = hp.playerId)
INNER JOIN Players p ON (p.id = hc.playerId) INNER JOIN Players p ON (p.id = hc.playerId)
WHERE h.id = %s WHERE h.id = %s
AND hc.styleKey > %s AND hc.styleKey > %s
/* styleKey is currently 'd' (for date) followed by a yyyymmdd /* styleKey is currently 'd' (for date) followed by a yyyymmdd
date key. Set it to 0000000 or similar to get all records */ 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+ ?? /* Note: s means the placeholder 'percent's but we can't include that
in comments. (db api thinks they are actual arguments)
Could also check activeseats here even if only 3 groups eg 2-3/4-6/7+
e.g. could use a multiplier: e.g. could use a multiplier:
AND h.seats > %s / 1.25 and hp.seats < %s * 1.25 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 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) 1.25 would be a config value so user could change it)
*/ */
AND hc.gametypeId+0 in AND hc.gametypeId+0 in
(SELECT gt1.id from Gametypes gt1, Gametypes gt2 (SELECT gt1.id from Gametypes gt1, Gametypes gt2
WHERE gt1.siteid = gt2.siteid WHERE gt1.siteid = gt2.siteid /* find gametypes where these match: */
AND gt1.type = gt2.type AND gt1.type = gt2.type /* ring/tourney */
AND gt1.category = gt2.category AND gt1.category = gt2.category /* holdem/stud*/
AND gt1.limittype = gt2.limittype AND gt1.limittype = gt2.limittype /* fl/nl */
AND gt1.bigblind < gt2.bigblind * %s /* bigblind similar size */
AND gt1.bigblind > gt2.bigblind / %s
AND gt2.id = h.gametypeId) AND gt2.id = h.gametypeId)
GROUP BY hc.PlayerId, p.name, hc.styleKey GROUP BY hc.PlayerId, p.name
""" """
if db_server == 'mysql': if db_server == 'mysql':
@ -1249,7 +1483,7 @@ class Sql:
INNER JOIN HandsPlayers hp2 ON (hp2.playerId+0 = hp.playerId+0 AND (hp2.handId = h2.id+0)) /* other hands by these players */ 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) INNER JOIN Players p ON (p.id = hp2.PlayerId+0)
WHERE hp.handId = %s WHERE hp.handId = %s
/* check activeseats once this data returned? (don't want to do that here as it might /* 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) assume a session ended just because the number of seats dipped for a few hands)
*/ */
ORDER BY h.handStart desc, hp2.PlayerId ORDER BY h.handStart desc, hp2.PlayerId
@ -1328,7 +1562,7 @@ class Sql:
AND hp2.handId = h2.id) /* other hands by these players */ AND hp2.handId = h2.id) /* other hands by these players */
INNER JOIN Players p ON (p.id = hp2.PlayerId+0) INNER JOIN Players p ON (p.id = hp2.PlayerId+0)
WHERE h.id = %s WHERE h.id = %s
/* check activeseats once this data returned? (don't want to do that here as it might /* 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) assume a session ended just because the number of seats dipped for a few hands)
*/ */
ORDER BY h.handStart desc, hp2.PlayerId ORDER BY h.handStart desc, hp2.PlayerId
@ -1407,16 +1641,50 @@ class Sql:
select coalesce(max(id),0) select coalesce(max(id),0)
from Hands from Hands
where handStart < date_sub(utc_timestamp(), interval '1' day)""" where handStart < date_sub(utc_timestamp(), interval '1' day)"""
else: # assume postgresql elif db_server == 'postgresql':
self.query['get_hand_1day_ago'] = """ self.query['get_hand_1day_ago'] = """
select coalesce(max(id),0) select coalesce(max(id),0)
from Hands from Hands
where handStart < now() at time zone 'UTC' - interval '1 day'""" where handStart < now() at time zone 'UTC' - interval '1 day'"""
#if db_server == 'mysql': # not used yet ...
self.query['get_hand_nhands_ago'] = """ # gets a date, would need to use handsplayers (not hudcache) to get exact hand Id
select coalesce(greatest(max(id),%s)-%s,0) if db_server == 'mysql':
from Hands""" self.query['get_date_nhands_ago'] = """
select concat( 'd', date_format(max(h.handStart), '%Y%m%d') )
from (select hp.playerId
,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx
from HandsPlayers hp
where hp.playerId = %s
group by hp.playerId) hp2
inner join HandsPlayers hp3 on ( hp3.handId <= hp2.maxminusx
and hp3.playerId = hp2.playerId)
inner join Hands h on (h.id = hp3.handId)
"""
elif db_server == 'postgresql':
self.query['get_date_nhands_ago'] = """
select 'd' || to_char(max(h3.handStart), 'YYMMDD')
from (select hp.playerId
,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx
from HandsPlayers hp
where hp.playerId = %s
group by hp.playerId) hp2
inner join HandsPlayers hp3 on ( hp3.handId <= hp2.maxminusx
and hp3.playerId = hp2.playerId)
inner join Hands h on (h.id = hp3.handId)
"""
elif db_server == 'sqlite': # untested guess at query:
self.query['get_date_nhands_ago'] = """
select 'd' || strftime(max(h3.handStart), 'YYMMDD')
from (select hp.playerId
,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx
from HandsPlayers hp
where hp.playerId = %s
group by hp.playerId) hp2
inner join HandsPlayers hp3 on ( hp3.handId <= hp2.maxminusx
and hp3.playerId = hp2.playerId)
inner join Hands h on (h.id = hp3.handId)
"""
# used in GuiPlayerStats: # used in GuiPlayerStats:
self.query['getPlayerId'] = """SELECT id from Players where name = %s""" self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
@ -1587,7 +1855,7 @@ class Sql:
,upper(gt.limitType) ,upper(gt.limitType)
,s.name ,s.name
""" """
#elif db_server == 'sqlite': # what is the correct value here? #elif db_server == 'sqlite':
# self.query['playerDetailedStats'] = """ """ # self.query['playerDetailedStats'] = """ """
if db_server == 'mysql': if db_server == 'mysql':
@ -1799,7 +2067,7 @@ class Sql:
) hprof2 ) hprof2
on hprof2.gtId = stats.gtId on hprof2.gtId = stats.gtId
order by stats.base, stats.limittype, stats.bigBlindDesc desc <orderbyseats>""" order by stats.base, stats.limittype, stats.bigBlindDesc desc <orderbyseats>"""
#elif db_server == 'sqlite': # what is the correct value here? #elif db_server == 'sqlite':
# self.query['playerStats'] = """ """ # self.query['playerStats'] = """ """
if db_server == 'mysql': if db_server == 'mysql':
@ -2074,7 +2342,7 @@ class Sql:
order by stats.category, stats.limitType, stats.bigBlindDesc desc order by stats.category, stats.limitType, stats.bigBlindDesc desc
<orderbyseats>, cast(stats.PlPosition as smallint) <orderbyseats>, cast(stats.PlPosition as smallint)
""" """
#elif db_server == 'sqlite': # what is the correct value here? #elif db_server == 'sqlite':
# self.query['playerStatsByPosition'] = """ """ # self.query['playerStatsByPosition'] = """ """
self.query['getRingProfitAllHandsPlayerIdSite'] = """ self.query['getRingProfitAllHandsPlayerIdSite'] = """
@ -2413,6 +2681,10 @@ class Sql:
else: # assume postgres else: # assume postgres
self.query['lockForInsert'] = "" self.query['lockForInsert'] = ""
if db_server == 'sqlite':
for k,q in self.query.iteritems():
self.query[k] = re.sub('%s','?',q)
if __name__== "__main__": if __name__== "__main__":
# just print the default queries and exit # just print the default queries and exit
s = Sql(game = 'razz', type = 'ptracks') s = Sql(game = 'razz', type = 'ptracks')

View File

@ -17,6 +17,33 @@
import os import os
import sys import sys
import re
# if path is set to use an old version of python look for a new one:
# (does this work in linux?)
if os.name == 'nt' and sys.version[0:3] not in ('2.5', '2.6') and '-r' not in sys.argv:
#print "old path =", os.environ['PATH']
dirs = re.split(os.pathsep, os.environ['PATH'])
# remove any trailing / or \ chars from dirs:
dirs = [re.sub('[\\/]$','',p) for p in dirs]
# remove any dirs containing 'python' apart from those ending in 'python25', 'python26' or 'python':
dirs = [p for p in dirs if not re.search('python', p, re.I) or re.search('python25$', p, re.I) or re.search('python26$', p, re.I)]
tmppath = ";".join(dirs)
#print "new path =", tmppath
if re.search('python', tmppath, re.I):
os.environ['PATH'] = tmppath
print "Python " + sys.version[0:3] + ' - press return to continue\n'
sys.stdin.readline()
os.execvpe('python.exe', ('python.exe', 'fpdb.py', '-r'), os.environ) # first arg is ignored (name of program being run)
else:
print "\npython 2.5 not found, please install python 2.5 or 2.6 for fpdb\n"
exit
else:
pass
#print "debug - not changing path"
print "Python " + sys.version[0:3] + '...\n'
import threading import threading
import Options import Options
import string import string
@ -204,10 +231,10 @@ class fpdb:
# print 'User cancelled loading profile' # print 'User cancelled loading profile'
#except: #except:
# pass # pass
try: #try:
self.load_profile() self.load_profile()
except: #except:
pass # pass
self.release_global_lock() self.release_global_lock()
#end def dia_load_profile #end def dia_load_profile
@ -246,7 +273,6 @@ class fpdb:
def dia_recreate_hudcache(self, widget, data=None): def dia_recreate_hudcache(self, widget, data=None):
if self.obtain_global_lock(): if self.obtain_global_lock():
try:
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm recreating HUD cache") dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm recreating HUD cache")
diastring = "Please confirm that you want to re-create the HUD cache." diastring = "Please confirm that you want to re-create the HUD cache."
dia_confirm.format_secondary_text(diastring) dia_confirm.format_secondary_text(diastring)
@ -257,8 +283,6 @@ class fpdb:
self.db.rebuild_hudcache() self.db.rebuild_hudcache()
elif response == gtk.REPSONSE_NO: elif response == gtk.REPSONSE_NO:
print 'User cancelled rebuilding hud cache' print 'User cancelled rebuilding hud cache'
except:
pass
self.release_global_lock() self.release_global_lock()
@ -467,7 +491,7 @@ class fpdb:
def tab_auto_import(self, widget, data=None): 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.sql)
self.threads.append(new_aimp_thread) self.threads.append(new_aimp_thread)
aimp_tab=new_aimp_thread.get_vbox() aimp_tab=new_aimp_thread.get_vbox()
self.add_and_display_tab(aimp_tab, "Auto Import") self.add_and_display_tab(aimp_tab, "Auto Import")

View File

@ -64,7 +64,7 @@ class fpdb_db:
if backend==fpdb_db.MYSQL_INNODB: if backend==fpdb_db.MYSQL_INNODB:
import MySQLdb import MySQLdb
try: try:
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True) self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True, charset="utf8")
except: except:
raise fpdb_simple.FpdbError("MySQL connection failed") raise fpdb_simple.FpdbError("MySQL connection failed")
elif backend==fpdb_db.PGSQL: elif backend==fpdb_db.PGSQL:
@ -155,7 +155,7 @@ class fpdb_db:
return (self.host, self.database, self.user, self.password) return (self.host, self.database, self.user, self.password)
#end def get_db_info #end def get_db_info
def getLastInsertId(self): def getLastInsertId(self, cursor=None):
try: try:
if self.backend == self.MYSQL_INNODB: if self.backend == self.MYSQL_INNODB:
ret = self.db.insert_id() ret = self.db.insert_id()
@ -177,9 +177,7 @@ class fpdb_db:
else: else:
ret = row[0] ret = row[0]
elif self.backend == fpdb_db.SQLITE: elif self.backend == fpdb_db.SQLITE:
# don't know how to do this in sqlite ret = cursor.lastrowid
print "getLastInsertId(): not coded for sqlite yet"
ret = -1
else: else:
print "getLastInsertId(): unknown backend ", self.backend print "getLastInsertId(): unknown backend ", self.backend
ret = -1 ret = -1

View File

@ -21,12 +21,15 @@
import os # todo: remove this once import_dir is in fpdb_import import os # todo: remove this once import_dir is in fpdb_import
import sys import sys
from time import time, strftime from time import time, strftime, sleep
import logging import logging
import traceback import traceback
import math import math
import datetime import datetime
import re import re
import Queue
from collections import deque # using Queue for now
import threading
# fpdb/FreePokerTools modules # fpdb/FreePokerTools modules
@ -61,27 +64,36 @@ class Importer:
self.config = config self.config = config
self.sql = sql self.sql = sql
self.database = None # database will be the main db interface eventually
self.filelist = {} self.filelist = {}
self.dirlist = {} self.dirlist = {}
self.siteIds = {} 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.updatedsize = {}
self.updatedtime = {}
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")
# CONFIGURATION OPTIONS
self.settings.setdefault("minPrint", 30) self.settings.setdefault("minPrint", 30)
self.settings.setdefault("handCount", 0) self.settings.setdefault("handCount", 0)
#self.settings.setdefault("allowHudcacheRebuild", True) # NOT USED NOW
#self.settings.setdefault("forceThreads", 2) # NOT USED NOW
self.settings.setdefault("writeQSize", 1000) # no need to change
self.settings.setdefault("writeQMaxWait", 10) # not used
self.database = Database.Database(self.config, sql = self.sql) # includes .connection and .sql variables self.writeq = None
self.database = Database.Database(self.config, sql = self.sql)
self.writerdbs = []
self.settings.setdefault("threads", 1) # value set by GuiBulkImport
for i in xrange(self.settings['threads']):
self.writerdbs.append( Database.Database(self.config, sql = self.sql) )
self.NEWIMPORT = False self.NEWIMPORT = False
self.allow_hudcache_rebuild = False
#Set functions #Set functions
def setCallHud(self, value): def setCallHud(self, value):
@ -104,16 +116,27 @@ class Importer:
def setThreads(self, value): def setThreads(self, value):
self.settings['threads'] = value self.settings['threads'] = value
if self.settings["threads"] > len(self.writerdbs):
for i in xrange(self.settings['threads'] - len(self.writerdbs)):
self.writerdbs.append( Database.Database(self.config, sql = self.sql) )
def setDropIndexes(self, value): def setDropIndexes(self, value):
self.settings['dropIndexes'] = value self.settings['dropIndexes'] = value
def setDropHudCache(self, value):
self.settings['dropHudCache'] = value
# def setWatchTime(self): # def setWatchTime(self):
# self.updated = time() # self.updated = time()
def clearFileList(self): def clearFileList(self):
self.filelist = {} self.filelist = {}
def closeDBs(self):
self.database.disconnect()
for i in xrange(len(self.writerdbs)):
self.writerdbs[i].disconnect()
#Add an individual file to filelist #Add an individual file to filelist
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!!
@ -164,51 +187,99 @@ class Importer:
print "Warning: Attempted to add non-directory: '" + str(dir) + "' as an import directory" print "Warning: Attempted to add non-directory: '" + str(dir) + "' as an import directory"
def runImport(self): def runImport(self):
""""Run full import on self.filelist.""" """"Run full import on self.filelist. This is called from GuiBulkImport.py"""
#if self.settings['forceThreads'] > 0: # use forceThreads until threading enabled in GuiBulkImport
# self.setThreads(self.settings['forceThreads'])
# Initial setup
start = datetime.datetime.now() start = datetime.datetime.now()
starttime = time()
print "Started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes'] print "Started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes']
if self.settings['dropIndexes'] == 'auto': if self.settings['dropIndexes'] == 'auto':
self.settings['dropIndexes'] = self.calculate_auto2(12.0, 500.0) self.settings['dropIndexes'] = self.calculate_auto2(self.database, 12.0, 500.0)
if self.allow_hudcache_rebuild: if self.settings['dropHudCache'] == 'auto':
self.settings['dropHudCache'] = self.calculate_auto2(25.0, 500.0) # returns "drop"/"don't drop" self.settings['dropHudCache'] = self.calculate_auto2(self.database, 25.0, 500.0) # returns "drop"/"don't drop"
if self.settings['dropIndexes'] == 'drop': if self.settings['dropIndexes'] == 'drop':
self.database.prepareBulkImport() self.database.prepareBulkImport()
else: else:
print "No need to drop indexes." print "No need to drop indexes."
#print "dropInd =", self.settings['dropIndexes'], " dropHudCache =", self.settings['dropHudCache'] #print "dropInd =", self.settings['dropIndexes'], " dropHudCache =", self.settings['dropHudCache']
if self.settings['threads'] <= 0:
(totstored, totdups, totpartial, toterrors) = self.importFiles(self.database, None)
else:
# create queue (will probably change to deque at some point):
self.writeq = Queue.Queue( self.settings['writeQSize'] )
# start separate thread(s) to read hands from queue and write to db:
for i in xrange(self.settings['threads']):
t = threading.Thread( target=self.writerdbs[i].insert_queue_hands
, args=(self.writeq, self.settings["writeQMaxWait"])
, name="dbwriter-"+str(i) )
t.setDaemon(True)
t.start()
# read hands and write to q:
(totstored, totdups, totpartial, toterrors) = self.importFiles(self.database, self.writeq)
if self.writeq.empty():
print "writers finished already"
pass
else:
print "waiting for writers to finish ..."
#for t in threading.enumerate():
# print " "+str(t)
#self.writeq.join()
#using empty() might be more reliable:
while not self.writeq.empty() and len(threading.enumerate()) > 1:
sleep(0.5)
print " ... writers finished"
# Tidying up after import
if self.settings['dropIndexes'] == 'drop':
self.database.afterBulkImport()
else:
print "No need to rebuild indexes."
if self.settings['dropHudCache'] == 'drop':
self.database.rebuild_hudcache()
else:
print "No need to rebuild hudcache."
self.database.analyzeDB()
endtime = time()
return (totstored, totdups, totpartial, toterrors, endtime-starttime)
# end def runImport
def importFiles(self, db, q):
""""Read filenames in self.filelist and pass to import_file_dict().
Uses a separate database connection if created as a thread (caller
passes None or no param as db)."""
totstored = 0 totstored = 0
totdups = 0 totdups = 0
totpartial = 0 totpartial = 0
toterrors = 0 toterrors = 0
tottime = 0 tottime = 0
# if threads <= 1: do this bit
for file in self.filelist: for file in self.filelist:
(stored, duplicates, partial, errors, ttime) = self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) (stored, duplicates, partial, errors, ttime) = self.import_file_dict(db, file
,self.filelist[file][0], self.filelist[file][1], q)
totstored += stored totstored += stored
totdups += duplicates totdups += duplicates
totpartial += partial totpartial += partial
toterrors += errors toterrors += errors
tottime += ttime
if self.settings['dropIndexes'] == 'drop':
self.database.afterBulkImport()
else:
print "No need to rebuild indexes."
if self.allow_hudcache_rebuild and self.settings['dropHudCache'] == 'drop':
self.database.rebuild_hudcache()
else:
print "No need to rebuild hudcache."
self.database.analyzeDB()
return (totstored, totdups, totpartial, toterrors, tottime)
# else: import threaded
def calculate_auto(self): for i in xrange( self.settings['threads'] ):
print "sending finish msg qlen =", q.qsize()
db.send_finish_msg(q)
return (totstored, totdups, totpartial, toterrors)
# end def importFiles
# not used currently
def calculate_auto(self, db):
"""An heuristic to determine a reasonable value of drop/don't drop""" """An heuristic to determine a reasonable value of drop/don't drop"""
if len(self.filelist) == 1: return "don't drop" if len(self.filelist) == 1: return "don't drop"
if 'handsInDB' not in self.settings: if 'handsInDB' not in self.settings:
try: try:
tmpcursor = self.database.get_cursor() tmpcursor = db.get_cursor()
tmpcursor.execute("Select count(1) from Hands;") tmpcursor.execute("Select count(1) from Hands;")
self.settings['handsInDB'] = tmpcursor.fetchone()[0] self.settings['handsInDB'] = tmpcursor.fetchone()[0]
except: except:
@ -218,7 +289,7 @@ class Importer:
if self.settings['handsInDB'] > 50000: return "don't drop" if self.settings['handsInDB'] > 50000: return "don't drop"
return "drop" return "drop"
def calculate_auto2(self, scale, increment): def calculate_auto2(self, db, scale, increment):
"""A second heuristic to determine a reasonable value of drop/don't drop """A second heuristic to determine a reasonable value of drop/don't drop
This one adds up size of files to import to guess number of hands in them This one adds up size of files to import to guess number of hands in them
Example values of scale and increment params might be 10 and 500 meaning Example values of scale and increment params might be 10 and 500 meaning
@ -231,7 +302,7 @@ class Importer:
# get number of hands in db # get number of hands in db
if 'handsInDB' not in self.settings: if 'handsInDB' not in self.settings:
try: try:
tmpcursor = self.database.get_cursor() tmpcursor = db.get_cursor()
tmpcursor.execute("Select count(1) from Hands;") tmpcursor.execute("Select count(1) from Hands;")
self.settings['handsInDB'] = tmpcursor.fetchone()[0] self.settings['handsInDB'] = tmpcursor.fetchone()[0]
except: except:
@ -253,7 +324,7 @@ class Importer:
# size_per_hand, "inc =", increment, "return:", ret # size_per_hand, "inc =", increment, "return:", ret
return ret return ret
#Run import on updated files, then store latest update time. #Run import on updated files, then store latest update time. Called from GuiAutoImport.py
def runUpdated(self): def runUpdated(self):
#Check for new files in monitored directories #Check for new files in monitored directories
#todo: make efficient - always checks for new file, should be able to use mtime of directory #todo: make efficient - always checks for new file, should be able to use mtime of directory
@ -268,15 +339,18 @@ class Importer:
if os.path.exists(file): if os.path.exists(file):
stat_info = os.stat(file) stat_info = os.stat(file)
#rulog.writelines("path exists ") #rulog.writelines("path exists ")
if file in self.updated: if file in self.updatedsize: # we should be able to assume that if we're in size, we're in time as well
if stat_info.st_size > self.updated[file]: if stat_info.st_size > self.updatedsize[file] or stat_info.st_mtime > self.updatedtime[file]:
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(self.database, file, self.filelist[file][0], self.filelist[file][1], None)
self.updated[file] = stat_info.st_size self.updatedsize[file] = stat_info.st_size
self.updatedtime[file] = time()
else: else:
if os.path.isdir(file) or (time() - stat_info.st_mtime) < 60: if os.path.isdir(file) or (time() - stat_info.st_mtime) < 60:
self.updated[file] = 0 self.updatedsize[file] = 0
self.updatedtime[file] = 0
else: else:
self.updated[file] = stat_info.st_size self.updatedsize[file] = stat_info.st_size
self.updatedtime[file] = time()
else: else:
self.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)
@ -292,16 +366,21 @@ class Importer:
#rulog.close() #rulog.close()
# 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, db, file, site, filter, q=None):
#print "import_file_dict" #print "import_file_dict"
if os.path.isdir(file): if os.path.isdir(file):
self.addToDirList[file] = [site] + [filter] self.addToDirList[file] = [site] + [filter]
return return
conv = None conv = None
(stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0)
# Load filter, process file, pass returned filename to import_fpdb_file # Load filter, process file, pass returned filename to import_fpdb_file
print "\nConverting %s" % file if self.writeq != None:
print "\nConverting " + file + " (" + str(q.qsize()) + ")"
else:
print "\nConverting " + file
hhbase = self.config.get_import_parameters().get("hhArchiveBase") hhbase = self.config.get_import_parameters().get("hhArchiveBase")
hhbase = os.path.expanduser(hhbase) hhbase = os.path.expanduser(hhbase)
hhdir = os.path.join(hhbase,site) hhdir = os.path.join(hhbase,site)
@ -315,34 +394,34 @@ class Importer:
mod = __import__(filter) mod = __import__(filter)
obj = getattr(mod, filter_name, None) obj = getattr(mod, filter_name, None)
if callable(obj): if callable(obj):
conv = obj(in_path = file, out_path = out_path, index = 0) # Index into file 0 until changeover hhc = obj(in_path = file, out_path = out_path, index = 0) # Index into file 0 until changeover
if(conv.getStatus() and self.NEWIMPORT == False): if(hhc.getStatus() and self.NEWIMPORT == False):
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(out_path, site) (stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
elif (conv.getStatus() and self.NEWIMPORT == True): elif (hhc.getStatus() and self.NEWIMPORT == True):
#This code doesn't do anything yet #This code doesn't do anything yet
handlist = hhc.getProcessedHands() handlist = hhc.getProcessedHands()
self.pos_in_file[file] = hhc.getLastCharacterRead() self.pos_in_file[file] = hhc.getLastCharacterRead()
for hand in handlist: for hand in handlist:
hand.prepInsert() #hand.prepInsert()
hand.insert() hand.insert(self.database)
else: else:
# conversion didn't work # conversion didn't work
# TODO: appropriate response? # TODO: appropriate response?
return (0, 0, 0, 1, 0) return (0, 0, 0, 1, 0, -1)
else: else:
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter) print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
return return (0, 0, 0, 1, 0, -1)
#This will barf if conv.getStatus != True #This will barf if conv.getStatus != True
return (stored, duplicates, partial, errors, ttime) return (stored, duplicates, partial, errors, ttime)
def import_fpdb_file(self, file, site): def import_fpdb_file(self, db, file, site, q):
#print "import_fpdb_file"
starttime = time() starttime = time()
last_read_hand = 0 last_read_hand = 0
loc = 0 loc = 0
(stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0)
# print "file =", file # print "file =", file
if file == "stdin": if file == "stdin":
inputFile = sys.stdin inputFile = sys.stdin
@ -369,20 +448,46 @@ class Importer:
self.pos_in_file[file] = inputFile.tell() self.pos_in_file[file] = inputFile.tell()
inputFile.close() inputFile.close()
#self.database.lock_for_insert() # should be ok when using one thread (stored, duplicates, partial, errors, ttime, handsId) = self.import_fpdb_lines(db, self.lines, starttime, file, site, q)
db.commit()
ttime = time() - starttime
if q == None:
print "\rTotal stored:", stored, " duplicates:", duplicates, "errors:", errors, " time:", ttime
if not stored:
if duplicates:
for line_no in xrange(len(self.lines)):
if self.lines[line_no].find("Game #")!=-1:
final_game_line=self.lines[line_no]
handsId=fpdb_simple.parseSiteHandNo(final_game_line)
else:
print "failed to read a single hand from file:", inputFile
handsId=0
#todo: this will cause return of an unstored hand number if the last hand was error
self.handsId=handsId
return (stored, duplicates, partial, errors, ttime)
# end def import_fpdb_file
def import_fpdb_lines(self, db, lines, starttime, file, site, q = None):
"""Import an fpdb hand history held in the list lines, could be one hand or many"""
#db.lock_for_insert() # should be ok when using one thread, but doesn't help??
try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return. try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
firstline = self.lines[0] firstline = lines[0]
except: except:
# just skip the debug message and return silently: # just skip the debug message and return silently:
#print "DEBUG: import_fpdb_file: failed on self.lines[0]: '%s' '%s' '%s' '%s' " %( file, site, self.lines, loc) #print "DEBUG: import_fpdb_file: failed on lines[0]: '%s' '%s' '%s' '%s' " %( file, site, lines, loc)
return (0,0,0,1,0) return (0,0,0,1,0,0)
if firstline.find("Tournament Summary")!=-1: if firstline.find("Tournament Summary")!=-1:
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,0,0,1,0) return (0,0,0,1,0,0)
category=fpdb_simple.recogniseCategory(firstline) category=fpdb_simple.recogniseCategory(firstline)
@ -391,11 +496,13 @@ class Importer:
duplicates = 0 #counter duplicates = 0 #counter
partial = 0 #counter partial = 0 #counter
errors = 0 #counter errors = 0 #counter
ttime = 0
handsId = 0
for i in xrange (len(self.lines)): for i in xrange (len(lines)):
if (len(self.lines[i])<2): #Wierd way to detect for '\r\n' or '\n' if (len(lines[i])<2): #Wierd way to detect for '\r\n' or '\n'
endpos=i endpos=i
hand=self.lines[startpos:endpos] hand=lines[startpos:endpos]
if (len(hand[0])<2): if (len(hand[0])<2):
hand=hand[1:] hand=hand[1:]
@ -412,10 +519,10 @@ class Importer:
self.hand=hand self.hand=hand
try: try:
handsId = fpdb_parse_logic.mainParser( self.settings handsId = fpdb_parse_logic.mainParser( self.settings, self.siteIds[site]
, self.siteIds[site], category, hand , category, hand, self.config
, self.config, self.database ) , db, q )
self.database.commit() db.commit()
stored += 1 stored += 1
if self.callHud: if self.callHud:
@ -425,23 +532,23 @@ 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.database.rollback() 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.database.commit() #dont remove this, in case hand processing was cancelled. db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
else: else:
self.database.rollback() 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)
self.database.rollback() db.rollback()
if self.settings['failOnError']: if self.settings['failOnError']:
self.database.commit() #dont remove this, in case hand processing was cancelled. db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
if self.settings['minPrint']: if self.settings['minPrint']:
@ -455,22 +562,8 @@ class Importer:
print "Total stored:", stored, "duplicates:", duplicates, "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 return (stored, duplicates, partial, errors, ttime, handsId)
print "\rTotal stored:", stored, "duplicates:", duplicates, "errors:", errors, " time:", ttime # end def import_fpdb_lines
if not stored:
if duplicates:
for line_no in xrange(len(self.lines)):
if self.lines[line_no].find("Game #")!=-1:
final_game_line=self.lines[line_no]
handsId=fpdb_simple.parseSiteHandNo(final_game_line)
else:
print "failed to read a single hand from file:", inputFile
handsId=0
#todo: this will cause return of an unstored hand number if the last hand was error
self.database.commit()
self.handsId=handsId
return (stored, duplicates, partial, errors, ttime)
def printEmailErrorMessage(self, errors, filename, line): def printEmailErrorMessage(self, errors, filename, line):
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)

View File

@ -25,13 +25,12 @@ from time import time, strftime
#parses a holdem hand #parses a holdem hand
def mainParser(settings, siteID, category, hand, config, db = None): def mainParser(settings, siteID, category, hand, config, db = None, writeq = None):
#print "mainparser"
# fdb is not used now - to be removed ...
t0 = time() t0 = time()
#print "mainparser" #print "mainparser"
backend = settings['db-backend'] backend = settings['db-backend']
# Ideally db connection is passed in, if not use sql list if passed in, otherwise start from scratch
if db == None: if db == None:
db = Database.Database(c = config, sql = None) db = Database.Database(c = config, sql = None)
category = fpdb_simple.recogniseCategory(hand[0]) category = fpdb_simple.recogniseCategory(hand[0])
@ -80,7 +79,6 @@ def mainParser(settings, siteID, category, hand, config, db = None):
rebuyOrAddon = -1 rebuyOrAddon = -1
tourneyTypeId = 1 tourneyTypeId = 1
fpdb_simple.isAlreadyInDB(db.get_cursor(), gametypeID, siteHandNo) fpdb_simple.isAlreadyInDB(db.get_cursor(), gametypeID, siteHandNo)
hand = fpdb_simple.filterCrap(hand, isTourney) hand = fpdb_simple.filterCrap(hand, isTourney)
@ -182,7 +180,13 @@ def mainParser(settings, siteID, category, hand, config, db = None):
, positions, antes, cardValues, cardSuits, boardValues, boardSuits , positions, antes, cardValues, cardSuits, boardValues, boardSuits
, winnings, rakes, actionTypes, allIns, actionAmounts , winnings, rakes, actionTypes, allIns, actionAmounts
, actionNos, hudImportData, maxSeats, tableName, seatNos) , actionNos, hudImportData, maxSeats, tableName, seatNos)
# save hand in db via direct call or via q if in a thread
if writeq == None:
result = db.store_the_hand(htw) result = db.store_the_hand(htw)
else:
writeq.put(htw)
result = -999 # meaning unknown
t9 = time() t9 = time()
#print "parse and save=(%4.3f)" % (t9-t0) #print "parse and save=(%4.3f)" % (t9-t0)

View File

@ -49,37 +49,6 @@ class FpdbError(Exception):
def __str__(self): def __str__(self):
return repr(self.value) return repr(self.value)
# gets value for last auto-increment key generated
# returns -1 if a problem occurs
def getLastInsertId(backend, conn, cursor):
if backend == MYSQL_INNODB:
ret = conn.insert_id()
if ret < 1 or ret > 999999999:
print "getLastInsertId(): problem fetching insert_id? ret=", ret
ret = -1
elif backend == PGSQL:
# some options:
# currval(hands_id_seq) - use name of implicit seq here
# lastval() - still needs sequences set up?
# insert ... returning is useful syntax (but postgres specific?)
# see rules (fancy trigger type things)
cursor.execute ("SELECT lastval()")
row = cursor.fetchone()
if not row:
print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row
ret = -1
else:
ret = row[0]
elif backend == SQLITE:
# don't know how to do this in sqlite
print "getLastInsertId(): not coded for sqlite yet"
ret = -1
else:
print "getLastInsertId(): unknown backend ", backend
ret = -1
return ret
#end def getLastInsertId
#returns an array of the total money paid. intending to add rebuys/addons here #returns an array of the total money paid. intending to add rebuys/addons here
def calcPayin(count, buyin, fee): def calcPayin(count, buyin, fee):
return [buyin + fee for i in xrange(count)] return [buyin + fee for i in xrange(count)]

1
pyfpdb/makeexe.bat Normal file
View File

@ -0,0 +1 @@
python makeexe.py py2exe

10
pyfpdb/makeexe.py Normal file
View File

@ -0,0 +1,10 @@
from distutils.core import setup
import py2exe
opts = {
'py2exe': {
'includes': "pango,atk,gobject",
}
}
setup(name='Free Poker Database', version='0.12', console=[{"script":"fpdb.py"}])

47
pyfpdb/windows_make_bats.py Executable file
View File

@ -0,0 +1,47 @@
# create .bat scripts in windows to try out different gtk dirs
try:
import os
import sys
import re
if os.name != 'nt':
print "\nThis script is only for windows\n"
exit()
dirs = re.split(os.pathsep, os.environ['PATH'])
# remove any trailing / or \ chars from dirs:
dirs = [re.sub('[\\/]$','',p) for p in dirs]
# remove any dirs containing 'python' apart from those ending in 'python25', 'python26' or 'python':
dirs = [p for p in dirs if not re.search('python', p, re.I) or re.search('python25$', p, re.I) or re.search('python26$', p, re.I)]
# find gtk dirs:
gtkdirs = [p for p in dirs if re.search('gtk', p, re.I)]
lines = [ '@echo off\n\n'
, '<path goes here>'
, 'python fpdb.py\n\n'
, 'pause\n\n'
]
if gtkdirs:
i = 1
for gpath in gtkdirs: # enumerate converts the \\ into \
tmpdirs = [p for p in dirs if not re.search('gtk', p, re.I) or p == gpath]
tmppath = ";".join(tmpdirs)
lines[1] = 'PATH=' + tmppath + '\n\n'
bat = open('run_fpdb'+str(i)+'.bat', 'w')
bat.writelines(lines)
bat.close()
i = i + 1
else:
print "\nno gtk directories found in your path - install gtk or edit the path manually\n"
except SystemExit:
pass
except:
print "Error:", str(sys.exc_info())
pass
# sys.stdin.readline()

42
utils/fix_table_desc.py Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/python
import re
desc = """
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| tourneyId | int(10) unsigned | NO | MUL | NULL | |
| playerId | int(10) unsigned | NO | MUL | NULL | |
| payinAmount | int(11) | NO | | NULL | |
| rank | int(11) | NO | | NULL | |
| winnings | int(11) | NO | | NULL | |
| comment | text | YES | | NULL | |
| commentTs | datetime | YES | | NULL | |
+-------------+---------------------+------+-----+---------+----------------+
"""
table = """
{| border="1"
|+Gametypes Table
"""
# get rid of the verticle spacing and clean up
desc = re.sub("[\+\-]+", "", desc)
desc = re.sub("^\n+", "", desc) # there's probably a better way
desc = re.sub("\n\n", "\n", desc)
# the first line is the header info
temp, desc = re.split("\n", desc, 1)
temp = re.sub("\|", "!", temp)
temp = re.sub(" !", " !!", temp)
table += temp + " Comments\n"
# the rest is he body of the table
for line in re.split("\n", desc):
line = re.sub(" \|", " ||", line)
table += "|+\n" + line + "\n"
table += "|}\n"
print table