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

Conflicts:

	pyfpdb/PokerStarsToFpdb.py
 - this fails py.test
This commit is contained in:
Matt Turnbull 2009-03-29 12:27:00 +01:00
commit f3ee865721
16 changed files with 346 additions and 352 deletions

View File

@ -48,7 +48,7 @@ class Layout:
self.height = int( node.getAttribute('height') ) self.height = int( node.getAttribute('height') )
self.location = [] self.location = []
self.location = map(lambda x: None, range(self.max+1)) # there must be a better way to do this? self.location = map(lambda x: None, range(self.max+1)) # fill array with max seats+1 empty entries
for location_node in node.getElementsByTagName('location'): for location_node in node.getElementsByTagName('location'):
if location_node.getAttribute('seat') != "": if location_node.getAttribute('seat') != "":
@ -230,13 +230,14 @@ class Tv:
(self.combinedStealFold, self.combined2B3B, self.combinedPostflop) ) (self.combinedStealFold, self.combined2B3B, self.combinedPostflop) )
class Config: class Config:
def __init__(self, file = None): def __init__(self, file = None, dbname = 'fpdb'):
# "file" is a path to an xml file with the fpdb/HUD configuration # "file" is a path to an xml file with the fpdb/HUD configuration
# we check the existence of "file" and try to recover if it doesn't exist # we check the existence of "file" and try to recover if it doesn't exist
self.dbname = dbname
self.default_config_path = self.get_default_config_path() self.default_config_path = self.get_default_config_path()
if not file == None: # configuration file path has been passed if file != None: # configuration file path has been passed
if not os.path.exists(file): if not os.path.exists(file):
print "Configuration file %s not found. Using defaults." % (file) print "Configuration file %s not found. Using defaults." % (file)
sys.stderr.write("Configuration file %s not found. Using defaults." % (file)) sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
@ -247,7 +248,7 @@ class Config:
if file == None: # no config file in the normal places if file == None: # no config file in the normal places
file = self.find_example_config() #Look for an example file to edit file = self.find_example_config() #Look for an example file to edit
if not file == None: if file != None:
pass pass
if file == None: # that didn't work either, just die if file == None: # that didn't work either, just die
@ -308,7 +309,7 @@ class Config:
tv = Tv(node = tv_node) tv = Tv(node = tv_node)
self.tv = tv self.tv = tv
db = self.get_db_parameters('fpdb') db = self.get_db_parameters()
if db['db-password'] == 'YOUR MYSQL PASSWORD': if db['db-password'] == 'YOUR MYSQL PASSWORD':
df_file = self.find_default_conf() df_file = self.find_default_conf()
if df_file == None: # this is bad if df_file == None: # this is bad
@ -411,7 +412,7 @@ class Config:
return location_node return location_node
def save(self, file = None): def save(self, file = None):
if not file == None: if file != None:
f = open(file, 'w') f = open(file, 'w')
self.doc.writexml(f) self.doc.writexml(f)
f.close() f.close()
@ -448,9 +449,9 @@ class Config:
else: else:
self.aux_windows[aux_name].layout[max].location[i] = ( locations[i][0], locations[i][1] ) self.aux_windows[aux_name].layout[max].location[i] = ( locations[i][0], locations[i][1] )
def get_db_parameters(self, name = None): def get_db_parameters(self):
if name == None: name = 'fpdb'
db = {} db = {}
name = self.dbname
try: db['db-databaseName'] = name try: db['db-databaseName'] = name
except: pass except: pass
@ -476,18 +477,18 @@ class Config:
def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None, def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
db_pass = None, db_server = None, db_type = None): db_pass = None, db_server = None, db_type = None):
db_node = self.get_db_node(db_name) db_node = self.get_db_node(db_name)
if not db_node == None: if db_node != None:
if not db_ip == None: db_node.setAttribute("db_ip", db_ip) if db_ip != None: db_node.setAttribute("db_ip", db_ip)
if not db_user == None: db_node.setAttribute("db_user", db_user) if db_user != None: db_node.setAttribute("db_user", db_user)
if not db_pass == None: db_node.setAttribute("db_pass", db_pass) if db_pass != None: db_node.setAttribute("db_pass", db_pass)
if not db_server == None: db_node.setAttribute("db_server", db_server) if db_server != None: db_node.setAttribute("db_server", db_server)
if not db_type == None: db_node.setAttribute("db_type", db_type) if db_type != None: db_node.setAttribute("db_type", db_type)
if self.supported_databases.has_key(db_name): if self.supported_databases.has_key(db_name):
if not db_ip == None: self.supported_databases[db_name].dp_ip = db_ip if db_ip != None: self.supported_databases[db_name].dp_ip = db_ip
if not db_user == None: self.supported_databases[db_name].dp_user = db_user if db_user != None: self.supported_databases[db_name].dp_user = db_user
if not db_pass == None: self.supported_databases[db_name].dp_pass = db_pass if db_pass != None: self.supported_databases[db_name].dp_pass = db_pass
if not db_server == None: self.supported_databases[db_name].dp_server = db_server if db_server != None: self.supported_databases[db_name].dp_server = db_server
if not db_type == None: self.supported_databases[db_name].dp_type = db_type if db_type != None: self.supported_databases[db_name].dp_type = db_type
return return
def get_tv_parameters(self): def get_tv_parameters(self):
@ -615,19 +616,19 @@ class Config:
font = None, font_size = None): font = None, font_size = None):
"""Sets the specified site parameters for the specified site.""" """Sets the specified site parameters for the specified site."""
site_node = self.get_site_node(site_name) site_node = self.get_site_node(site_name)
if not db_node == None: if db_node != None:
if not converter == None: site_node.setAttribute("converter", converter) if converter != None: site_node.setAttribute("converter", converter)
if not decoder == None: site_node.setAttribute("decoder", decoder) if decoder != None: site_node.setAttribute("decoder", decoder)
if not hudbgcolor == None: site_node.setAttribute("hudbgcolor", hudbgcolor) if hudbgcolor != None: site_node.setAttribute("hudbgcolor", hudbgcolor)
if not hudfgcolor == None: site_node.setAttribute("hudfgcolor", hudfgcolor) if hudfgcolor != None: site_node.setAttribute("hudfgcolor", hudfgcolor)
if not hudopacity == None: site_node.setAttribute("hudopacity", hudopacity) if hudopacity != None: site_node.setAttribute("hudopacity", hudopacity)
if not screen_name == None: site_node.setAttribute("screen_name", screen_name) if screen_name != None: site_node.setAttribute("screen_name", screen_name)
if not site_path == None: site_node.setAttribute("site_path", site_path) if site_path != None: site_node.setAttribute("site_path", site_path)
if not table_finder == None: site_node.setAttribute("table_finder", table_finder) if table_finder != None: site_node.setAttribute("table_finder", table_finder)
if not HH_path == None: site_node.setAttribute("HH_path", HH_path) if HH_path != None: site_node.setAttribute("HH_path", HH_path)
if not enabled == None: site_node.setAttribute("enabled", enabled) if enabled != None: site_node.setAttribute("enabled", enabled)
if not font == None: site_node.setAttribute("font", font) if font != None: site_node.setAttribute("font", font)
if not font_size == None: site_node.setAttribute("font_size", font_size) if font_size != None: site_node.setAttribute("font_size", font_size)
return return
def get_aux_windows(self): def get_aux_windows(self):

View File

@ -599,37 +599,6 @@ class FpdbSQLQueries:
################################ ################################
# Returns all cash game handIds and the money won(winnings is the final pot)
# by the playerId for a single site.
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getRingWinningsAllGamesPlayerIdSite'] = """SELECT handId, winnings FROM HandsPlayers
INNER JOIN Players ON HandsPlayers.playerId = Players.id
INNER JOIN Hands ON Hands.id = HandsPlayers.handId
WHERE Players.name = %s AND Players.siteId = %s AND (tourneysPlayersId IS NULL)
ORDER BY handStart"""
elif(self.dbname == 'SQLite'):
#Probably doesn't work.
self.query['getRingWinningsAllGamesPlayerIdSite'] = """SELECT handId, winnings FROM HandsPlayers
INNER JOIN Players ON HandsPlayers.playerId = Players.id
INNER JOIN Hands ON Hands.id = HandsPlayers.handId
WHERE Players.name = %s AND Players.siteId = %s AND (tourneysPlayersId IS NULL)
ORDER BY handStart"""
# Returns the profit for a given ring game handId, Total pot - money invested by playerId
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
INNER JOIN Players ON HandsPlayers.playerId = Players.id
WHERE Players.name = %s AND HandsPlayers.handId = %s
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
elif(self.dbname == 'SQLite'):
#Probably doesn't work.
self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
INNER JOIN Players ON HandsPlayers.playerId = Players.id
WHERE Players.name = %s AND HandsPlayers.handId = %s
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getPlayerId'] = """SELECT id from Players where name = %s""" self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):

View File

@ -238,7 +238,7 @@ def main(argv=None):
if os.name == 'nt': settings['os'] = 'windows' if os.name == 'nt': settings['os'] = 'windows'
else: settings['os'] = 'linuxmac' else: settings['os'] = 'linuxmac'
settings.update(config.get_db_parameters('fpdb')) settings.update(config.get_db_parameters())
settings.update(config.get_tv_parameters()) settings.update(config.get_tv_parameters())
settings.update(config.get_import_parameters()) settings.update(config.get_import_parameters())
settings.update(config.get_default_paths()) settings.update(config.get_default_paths())

View File

@ -68,13 +68,13 @@ class GuiGraphViewer (threading.Thread):
if len(result) == 1: if len(result) == 1:
playerids.append(result[0][0]) playerids.append(result[0][0])
if sitenos == []: if not sitenos:
#Should probably pop up here. #Should probably pop up here.
print "No sites selected - defaulting to PokerStars" print "No sites selected - defaulting to PokerStars"
sitenos = [2] sitenos = [2]
if playerids == []: if not playerids:
print "No player ids found" print "No player ids found"
return return

View File

@ -91,7 +91,7 @@ class HUD_main(object):
gtk.gdk.threads_enter() gtk.gdk.threads_enter()
try: try:
newlabel = gtk.Label(table.site + " - " + table_name) newlabel = gtk.Label("%s - %s" % (table.site, table_name))
self.vb.add(newlabel) self.vb.add(newlabel)
newlabel.show() newlabel.show()
self.main_window.resize_children() self.main_window.resize_children()
@ -110,8 +110,8 @@ class HUD_main(object):
self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection) self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection)
self.hud_dict[table_name].stat_dict = stat_dict self.hud_dict[table_name].stat_dict = stat_dict
self.hud_dict[table_name].cards = cards self.hud_dict[table_name].cards = cards
for aw in self.hud_dict[table_name].aux_windows:
aw.update_data(new_hand_id, self.db_connection) [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows]
gobject.idle_add(idle_func) gobject.idle_add(idle_func)
def update_HUD(self, new_hand_id, table_name, config): def update_HUD(self, new_hand_id, table_name, config):
@ -153,7 +153,8 @@ class HUD_main(object):
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id) stat_dict = self.db_connection.get_stats_from_hand(new_hand_id)
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)
cards['common'] = comm_cards['common'] if comm_cards != {}: # stud!
cards['common'] = comm_cards['common']
except Exception, err: except Exception, err:
print "db error: skipping ", new_hand_id, err print "db error: skipping ", new_hand_id, err
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)))

View File

@ -82,7 +82,7 @@ class Hud:
font = "Sans" font = "Sans"
if font_size == None: if font_size == None:
font_size = "8" font_size = "8"
self.font = pango.FontDescription(font + " " + font_size) self.font = pango.FontDescription("%s %s" % (font, font_size))
# do we need to add some sort of condition here for dealing with a request for a font that doesn't exist? # do we need to add some sort of condition here for dealing with a request for a font that doesn't exist?
game_params = config.get_game_parameters(self.poker_game) game_params = config.get_game_parameters(self.poker_game)
@ -102,6 +102,7 @@ class Hud:
self.main_window.set_title("%s FPDBHUD" % (self.table.name)) self.main_window.set_title("%s FPDBHUD" % (self.table.name))
self.main_window.set_decorated(False) self.main_window.set_decorated(False)
self.main_window.set_opacity(self.colors["hudopacity"]) self.main_window.set_opacity(self.colors["hudopacity"])
self.main_window.set_focus_on_map(False)
self.ebox = gtk.EventBox() self.ebox = gtk.EventBox()
self.label = gtk.Label("FPDB Menu (Right Click)\nLeft-drag to move") self.label = gtk.Label("FPDB Menu (Right Click)\nLeft-drag to move")
@ -173,10 +174,10 @@ class Hud:
self.main_window.move(x, y) self.main_window.move(x, y)
adj = self.adj_seats(self.hand, self.config) adj = self.adj_seats(self.hand, self.config)
loc = self.config.get_locations(self.table.site, self.max) loc = self.config.get_locations(self.table.site, self.max)
for i in xrange(1, self.max + 1): # TODO: is stat_windows getting converted somewhere from a list to a dict, for no good reason?
for i, w in enumerate(self.stat_windows.itervalues()):
(x, y) = loc[adj[i]] (x, y) = loc[adj[i]]
if i in self.stat_windows: w.relocate(x, y)
self.stat_windows[i].relocate(x, y)
return True return True
def on_button_press(self, widget, event): def on_button_press(self, widget, event):
@ -196,12 +197,12 @@ class Hud:
s.window.destroy() s.window.destroy()
self.stat_windows = {} self.stat_windows = {}
# also kill any aux windows # also kill any aux windows
[aux.destroy() for aux in self.aux_windows] (aux.destroy() for aux in self.aux_windows)
self.aux_windows = [] self.aux_windows = []
def reposition_windows(self, *args): def reposition_windows(self, *args):
if self.stat_windows != {} and len(self.stat_windows) > 0: if self.stat_windows != {} and len(self.stat_windows) > 0:
map(lambda x: x.window.move(x.x, x.y), self.stat_windows.itervalues()) (x.window.move(x.x, x.y) for x in self.stat_windows.itervalues() if type(x) != int)
return True return True
def debug_stat_windows(self, *args): def debug_stat_windows(self, *args):
@ -217,7 +218,7 @@ class Hud:
new_layout[self.stat_windows[sw].adj - 1] = new_loc new_layout[self.stat_windows[sw].adj - 1] = new_loc
self.config.edit_layout(self.table.site, self.max, locations = new_layout) self.config.edit_layout(self.table.site, self.max, locations = new_layout)
# ask each aux to save its layout back to the config object # ask each aux to save its layout back to the config object
[aux.save_layout() for aux in self.aux_windows] (aux.save_layout() for aux in self.aux_windows)
# save the config object back to the file # save the config object back to the file
print "saving new xml file" print "saving new xml file"
self.config.save() self.config.save()
@ -227,9 +228,9 @@ class Hud:
# Need range here, not xrange -> need the actual list # Need range here, not xrange -> need the actual list
adj = range(0, self.max + 1) # default seat adjustments = no adjustment adj = range(0, self.max + 1) # default seat adjustments = no adjustment
# does the user have a fav_seat? # does the user have a fav_seat?
try: if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
sys.stderr.write("site = %s, max = %d, fav seat = %d\n" % (self.table.site, self.max, config.supported_sites[self.table.site].layout[self.max].fav_seat)) try:
if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0: sys.stderr.write("site = %s, max = %d, fav seat = %d\n" % (self.table.site, self.max, config.supported_sites[self.table.site].layout[self.max].fav_seat))
fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
sys.stderr.write("found fav seat = %d\n" % fav_seat) sys.stderr.write("found fav seat = %d\n" % fav_seat)
# actual_seat = self.db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name) # actual_seat = self.db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name)
@ -237,12 +238,14 @@ class Hud:
sys.stderr.write("found actual seat = %d\n" % actual_seat) sys.stderr.write("found actual seat = %d\n" % actual_seat)
for i in xrange(0, self.max + 1): for i in xrange(0, self.max + 1):
j = actual_seat + i j = actual_seat + i
if j > self.max: j = j - self.max if j > self.max:
j = j - self.max
adj[j] = fav_seat + i adj[j] = fav_seat + i
if adj[j] > self.max: adj[j] = adj[j] - self.max if adj[j] > self.max:
except Exception, inst: adj[j] = adj[j] - self.max
sys.stderr.write("exception in adj!!!\n\n") except Exception, inst:
sys.stderr.write("error is %s" % inst) # __str__ allows args to printed directly sys.stderr.write("exception in adj!!!\n\n")
sys.stderr.write("error is %s" % inst) # __str__ allows args to printed directly
return adj return adj
def get_actual_seat(self, name): def get_actual_seat(self, name):
@ -286,10 +289,12 @@ class Hud:
font = self.font) font = self.font)
self.stats = [] self.stats = []
for i in xrange(0, config.supported_games[self.poker_game].rows + 1): game = config.supported_games[self.poker_game]
row_list = [''] * config.supported_games[self.poker_game].cols
for i in xrange(0, game.rows + 1):
row_list = [''] * game.cols
self.stats.append(row_list) self.stats.append(row_list)
for stat in config.supported_games[self.poker_game].stats: for stat in game.stats:
self.stats[config.supported_games[self.poker_game].stats[stat].row] \ self.stats[config.supported_games[self.poker_game].stats[stat].row] \
[config.supported_games[self.poker_game].stats[stat].col] = \ [config.supported_games[self.poker_game].stats[stat].col] = \
config.supported_games[self.poker_game].stats[stat].stat_name config.supported_games[self.poker_game].stats[stat].stat_name
@ -303,30 +308,30 @@ class Hud:
self.update_table_position() self.update_table_position()
for s in self.stat_dict: for s in self.stat_dict:
statd = self.stat_dict[s]
try: try:
self.stat_windows[self.stat_dict[s]['seat']].player_id = self.stat_dict[s]['player_id'] self.stat_windows[self.stat_dict[s]['seat']].player_id = self.stat_dict[s]['player_id']
except: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here except: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here
self.max = 10 self.max = 10
self.create(hand, config, self.stat_dict, self.cards) self.create(hand, config, self.stat_dict, self.cards)
self.stat_windows[self.stat_dict[s]['seat']].player_id = self.stat_dict[s]['player_id'] self.stat_windows[statd['seat']].player_id = statd['player_id']
for r in xrange(0, config.supported_games[self.poker_game].rows): for r in xrange(0, config.supported_games[self.poker_game].rows):
for c in xrange(0, config.supported_games[self.poker_game].cols): for c in xrange(0, config.supported_games[self.poker_game].cols):
this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]] this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]]
number = Stats.do_stat(self.stat_dict, player = self.stat_dict[s]['player_id'], stat = self.stats[r][c]) number = Stats.do_stat(self.stat_dict, player = statd['player_id'], stat = self.stats[r][c])
statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix statstring = "%s%s%s" % (this_stat.hudprefix, str(number[1]), this_stat.hudsuffix)
window = self.stat_windows[statd['seat']]
if this_stat.hudcolor != "": if this_stat.hudcolor != "":
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
self.stat_windows[self.stat_dict[s]['seat']].label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor)) window.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor))
self.stat_windows[self.stat_dict[s]['seat']].label[r][c].set_text(statstring) window.label[r][c].set_text(statstring)
if statstring != "xxx": # is there a way to tell if this particular stat window is visible already, or no? if statstring != "xxx": # is there a way to tell if this particular stat window is visible already, or no?
self.stat_windows[self.stat_dict[s]['seat']].window.show_all() window.window.show_all()
# self.reposition_windows() tip = "%s\n%s\n%s, %s" % (statd['screen_name'], number[5], number[3], number[4])
tip = self.stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \ Stats.do_tip(window.e_box[r][c], tip)
number[3] + ", " + number[4]
Stats.do_tip(self.stat_windows[self.stat_dict[s]['seat']].e_box[r][c], tip)
def topify_window(self, window): def topify_window(self, window):
"""Set the specified gtk window to stayontop in MS Windows.""" """Set the specified gtk window to stayontop in MS Windows."""
@ -407,9 +412,11 @@ class Stat_Window:
self.window.set_title("%s" % seat) self.window.set_title("%s" % seat)
self.window.set_property("skip-taskbar-hint", True) self.window.set_property("skip-taskbar-hint", True)
self.window.set_transient_for(parent.main_window) self.window.set_transient_for(parent.main_window)
self.window.set_focus_on_map(False)
self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False) grid = gtk.Table(rows = game.rows, columns = game.cols, homogeneous = False)
self.window.add(self.grid) self.grid = grid
self.window.add(grid)
self.window.modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) self.window.modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
self.e_box = [] self.e_box = []
@ -418,12 +425,12 @@ class Stat_Window:
usegtkframes = self.useframes usegtkframes = self.useframes
e_box = self.e_box e_box = self.e_box
label = self.label label = self.label
for r in xrange(self.game.rows): for r in xrange(game.rows):
if usegtkframes: if usegtkframes:
self.frame.append([]) self.frame.append([])
e_box.append([]) e_box.append([])
label.append([]) label.append([])
for c in xrange(self.game.cols): for c in xrange(game.cols):
if usegtkframes: if usegtkframes:
self.frame[r].append( gtk.Frame() ) self.frame[r].append( gtk.Frame() )
e_box[r].append( gtk.EventBox() ) e_box[r].append( gtk.EventBox() )
@ -433,10 +440,10 @@ class Stat_Window:
Stats.do_tip(e_box[r][c], 'stuff') Stats.do_tip(e_box[r][c], 'stuff')
if usegtkframes: if usegtkframes:
self.grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
self.frame[r][c].add(e_box[r][c]) self.frame[r][c].add(e_box[r][c])
else: else:
self.grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
label[r].append( gtk.Label('xxx') ) label[r].append( gtk.Label('xxx') )
if usegtkframes: if usegtkframes:

View File

@ -39,15 +39,19 @@ import Database
class Aux_Window: class Aux_Window:
def __init__(self, hud, params, config): def __init__(self, hud, params, config):
self.hud = hud self.hud = hud
self.params = params
self.config = config self.config = config
def update_data(self, *parms): def update_data(self, *args):
pass pass
def update_gui(self, *parms): def update_gui(self, *args):
pass pass
def create(self, *parms): def create(self, *args):
pass
def relocate(self, *args):
pass pass
def save_layout(self, *args): def save_layout(self, *args):
@ -321,6 +325,7 @@ class Flop_Mucked(Aux_Window):
self.config = config # configuration object for this aux window to use self.config = config # configuration object for this aux window to use
self.params = params # dict aux params from config self.params = params # dict aux params from config
self.positions = {} # dict of window positions self.positions = {} # dict of window positions
# self.rel_positions = {} # dict of window positions, relative to the table origin
self.displayed_cards = False self.displayed_cards = False
self.timer_on = False # bool = Ture if the timeout for removing the cards is on self.timer_on = False # bool = Ture if the timeout for removing the cards is on
self.card_images = self.get_card_images() self.card_images = self.get_card_images()
@ -345,11 +350,13 @@ class Flop_Mucked(Aux_Window):
self.m_windows[i].set_focus_on_map(False) self.m_windows[i].set_focus_on_map(False)
self.eb[i] = gtk.EventBox() self.eb[i] = gtk.EventBox()
self.eb[i].connect("button_press_event", self.button_press_cb) self.eb[i].connect("button_press_event", self.button_press_cb)
self.m_windows[i].connect("configure_event", self.configure_event_cb, i)
self.m_windows[i].add(self.eb[i]) self.m_windows[i].add(self.eb[i])
self.seen_cards[i] = gtk.image_new_from_pixbuf(self.card_images[('B', 'H')]) self.seen_cards[i] = gtk.image_new_from_pixbuf(self.card_images[('B', 'H')])
self.eb[i].add(self.seen_cards[i]) self.eb[i].add(self.seen_cards[i])
self.m_windows[i].move(int(x) + self.hud.table.x, int(y) + self.hud.table.y)
self.positions[i] = (int(x) + self.hud.table.x, int(y) + self.hud.table.y) self.positions[i] = (int(x) + self.hud.table.x, int(y) + self.hud.table.y)
# self.rel_positions[i] = (int(x), int(y))
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
self.m_windows[i].set_opacity(float(self.params['opacity'])) self.m_windows[i].set_opacity(float(self.params['opacity']))
self.m_windows[i].show_all() self.m_windows[i].show_all()
self.m_windows[i].hide() self.m_windows[i].hide()
@ -375,7 +382,7 @@ class Flop_Mucked(Aux_Window):
self.seen_cards[i].set_from_pixbuf(scratch) self.seen_cards[i].set_from_pixbuf(scratch)
# self.m_windows[i].show_all() # self.m_windows[i].show_all()
self.m_windows[i].resize(1,1) self.m_windows[i].resize(1,1)
self.m_windows[i].present() self.m_windows[i].show()
self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back
self.displayed_cards = True self.displayed_cards = True
@ -405,9 +412,8 @@ class Flop_Mucked(Aux_Window):
def hide_mucked_cards(self): def hide_mucked_cards(self):
"""Hide the mucked card windows.""" """Hide the mucked card windows."""
for (i, w) in self.m_windows.iteritems(): for (i, w) in self.m_windows.iteritems():
self.positions[i] = w.get_position()
w.hide() w.hide()
self.displayed_cards = False self.displayed_cards = False
def button_press_cb(self, widget, event, *args): def button_press_cb(self, widget, event, *args):
"""Handle button clicks in the event boxes.""" """Handle button clicks in the event boxes."""
@ -432,9 +438,13 @@ class Flop_Mucked(Aux_Window):
window = widget.get_parent() window = widget.get_parent()
window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time) window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
def configure_event_cb(self, widget, event, i, *args):
self.positions[i] = widget.get_position()
# self.rel_positions[i] = (self.positions[i][0] - self.hud.table.x, self.positions[i][1] - self.hud.table.y)
def expose_all(self): def expose_all(self):
for (i, cards) in self.hud.cards.iteritems(): for (i, cards) in self.hud.cards.iteritems():
self.m_windows[i].present() self.m_windows[i].show()
self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back
self.displayed_cards = True self.displayed_cards = True
@ -447,8 +457,6 @@ class Flop_Mucked(Aux_Window):
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y) new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
else: else:
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y) new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
print "old locations =", self.params['layout'][self.hud.max]
print "saving locations =", new_locs
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs) self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
if __name__== "__main__": if __name__== "__main__":

View File

@ -128,14 +128,11 @@ follow : whether to tail -f the input"""
logging.debug("readHandInfo: %s" % info) logging.debug("readHandInfo: %s" % info)
for key in info: for key in info:
if key == 'DATETIME': if key == 'DATETIME':
datetime = info[key] #2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]
#2008/11/16 1:22:47 CET [2008/11/15 19:22:47 ET] #2008/08/17 - 01:14:43 (ET)
m2 = re.search(r".+\[(.+) ET\]", datetime) #2008/09/07 06:23:14 ET
if m2: datetime = m2.group(1) m2 = re.search("(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)", info[key])
#2009/02/26 - 15:22:55 ET datetime = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), m2.group('M'),m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S'))
datetime = datetime.replace(" - "," ") # some are like "2009/02/26 - 15:22:55 ET"
datetime = datetime.replace(" (ET)","") # kludge for now.
datetime = datetime.replace(" ET","") # kludge for now.
hand.starttime = time.strptime(datetime, "%Y/%m/%d %H:%M:%S") hand.starttime = time.strptime(datetime, "%Y/%m/%d %H:%M:%S")
if key == 'HID': if key == 'HID':
hand.handid = info[key] hand.handid = info[key]

View File

@ -219,7 +219,7 @@ def discover_nt(c):
else: else:
tw.site = "Unknown" tw.site = "Unknown"
sys.stderr.write("Found unknown table = %s" % tw.title) sys.stderr.write("Found unknown table = %s" % tw.title)
if not tw.site == "Unknown": if tw.site != "Unknown":
eval("%s(tw)" % c.supported_sites[tw.site].decoder) eval("%s(tw)" % c.supported_sites[tw.site].decoder)
else: else:
tw.name = "Unknown" tw.name = "Unknown"

View File

@ -23,6 +23,8 @@ from optparse import OptionParser
parser = OptionParser() parser = OptionParser()
parser.add_option("-x", "--errorsToConsole", action="store_true", parser.add_option("-x", "--errorsToConsole", action="store_true",
help="If passed error output will go to the console rather than .") help="If passed error output will go to the console rather than .")
parser.add_option("-d", "--databaseName", dest="dbname", default="fpdb",
help="Overrides the default database name")
(options, sys.argv) = parser.parse_args() (options, sys.argv) = parser.parse_args()
if not options.errorsToConsole: if not options.errorsToConsole:
@ -337,7 +339,7 @@ class fpdb:
new_ps_thread=GuiPositionalStats.GuiPositionalStats(self.db, self.config, self.querydict) new_ps_thread=GuiPositionalStats.GuiPositionalStats(self.db, self.config, self.querydict)
self.threads.append(new_ps_thread) self.threads.append(new_ps_thread)
ps_tab=new_ps_thread.get_vbox() ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Ppositional Stats") self.add_and_display_tab(ps_tab, "Positional Stats")
def tab_main_help(self, widget, data): def tab_main_help(self, widget, data):
@ -371,7 +373,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
def __init__(self): def __init__(self):
self.threads=[] self.threads=[]
self.db=None self.db=None
self.config = Configuration.Config() self.config = Configuration.Config(dbname=options.dbname)
self.load_profile() self.load_profile()
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

View File

@ -59,7 +59,7 @@ class fpdb_db:
self.database=database self.database=database
if backend==self.MYSQL_INNODB: if backend==self.MYSQL_INNODB:
import MySQLdb import MySQLdb
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database) self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
elif backend==self.PGSQL: elif backend==self.PGSQL:
import psycopg2 import psycopg2
# If DB connection is made over TCP, then the variables # If DB connection is made over TCP, then the variables

View File

@ -21,7 +21,7 @@
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 from time import time, strftime
import traceback import traceback
import math import math
import datetime import datetime
@ -67,12 +67,10 @@ class Importer:
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")
if 'minPrint' not in self.settings:
#TODO: Is this value in the xml file? self.settings.setdefault("minPrint", 30)
self.settings['minPrint'] = 30 self.settings.setdefault("handCount", 0)
if 'handCount' not in self.settings:
#TODO: Is this value in the xml file?
self.settings['handCount'] = 0
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.fdb.do_connect(self.config) self.fdb.do_connect(self.config)
@ -266,10 +264,10 @@ class Importer:
def import_fpdb_file(self, file, site): def import_fpdb_file(self, file, site):
starttime = time() starttime = time()
last_read_hand=0 last_read_hand = 0
loc = 0 loc = 0
if (file=="stdin"): if file == "stdin":
inputFile=sys.stdin inputFile = sys.stdin
else: else:
if os.path.exists(file): if os.path.exists(file):
inputFile = open(file, "rU") inputFile = open(file, "rU")
@ -282,7 +280,7 @@ class Importer:
pass pass
# Read input file into class and close file # Read input file into class and close file
inputFile.seek(loc) inputFile.seek(loc)
self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines()) self.lines = fpdb_simple.removeTrailingEOL(inputFile.readlines())
self.pos_in_file[file] = inputFile.tell() self.pos_in_file[file] = inputFile.tell()
inputFile.close() inputFile.close()
@ -298,29 +296,29 @@ class Importer:
#self.parseTourneyHistory() #self.parseTourneyHistory()
return 0 return 0
site=fpdb_simple.recogniseSite(firstline) site = fpdb_simple.recogniseSite(firstline)
category=fpdb_simple.recogniseCategory(firstline) category = fpdb_simple.recogniseCategory(firstline)
startpos=0 startpos = 0
stored=0 #counter stored = 0 #counter
duplicates=0 #counter duplicates = 0 #counter
partial=0 #counter partial = 0 #counter
errors=0 #counter errors = 0 #counter
for i in xrange (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method for i in xrange (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method
if (len(self.lines[i])<2): if len(self.lines[i]) < 2:
endpos=i endpos = i
hand=self.lines[startpos:endpos] hand = self.lines[startpos:endpos]
if (len(hand[0])<2): if len(hand[0]) < 2:
hand=hand[1:] hand = hand[1:]
cancelled=False cancelled=False
damaged=False damaged=False
if (site=="ftp"): if (site=="ftp"):
for i in range (len(hand)): for i in range (len(hand)):
if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right? if hand[i].endswith(" has been canceled"): #this is their typo. this is a typo, right?
cancelled=True cancelled = True
#FTP generates lines looking like: #FTP generates lines looking like:
#Seat 1: IOS Seat 2: kashman59 (big blind) showed [8c 9d] and won ($3.25) with a pair of Eights #Seat 1: IOS Seat 2: kashman59 (big blind) showed [8c 9d] and won ($3.25) with a pair of Eights
@ -334,15 +332,15 @@ class Importer:
hand.insert(i+1, hand[i][mo.start()+1:]) hand.insert(i+1, hand[i][mo.start()+1:])
hand[i] = hand[i][0:mo.start()] hand[i] = hand[i][0:mo.start()]
if (len(hand)<3): if len(hand) < 3:
pass pass
#todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work. #todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work.
elif (hand[0].endswith(" (partial)")): #partial hand - do nothing elif hand[0].endswith(" (partial)"): #partial hand - do nothing
partial+=1 partial += 1
elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and? elif "Seat" not in hand[1] and "Seat" not in hand[2] and "Seat" not in hand[3]:
partial+=1 partial += 1
elif (cancelled or damaged): elif cancelled or damaged:
partial+=1 partial += 1
if damaged: if damaged:
print """ print """
DEBUG: Partial hand triggered by a line containing 'Seat X:' twice. This is a DEBUG: Partial hand triggered by a line containing 'Seat X:' twice. This is a
@ -352,57 +350,58 @@ class Importer:
print "File: %s" %(file) print "File: %s" %(file)
print "Line: %s" %(startpos) print "Line: %s" %(startpos)
else: #normal processing else: #normal processing
isTourney=fpdb_simple.isTourney(hand[0]) isTourney = fpdb_simple.isTourney(hand[0])
if not isTourney: if not isTourney:
fpdb_simple.filterAnteBlindFold(site,hand) hand = fpdb_simple.filterAnteBlindFold(site,hand)
self.hand=hand self.hand=hand
try: try:
handsId=fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db handsId = fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db
,self.fdb.cursor, site, category, hand, self.config) ,self.fdb.cursor, site, category, hand, self.config)
self.fdb.db.commit() self.fdb.db.commit()
stored+=1 stored += 1
if self.callHud: if self.callHud:
#print "call to HUD here. handsId:",handsId #print "call to HUD here. handsId:",handsId
#pipe the Hands.id out to the HUD #pipe the Hands.id out to the HUD
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
except (ValueError), fe: except (ValueError), fe:
errors+=1 errors += 1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.fdb.db.commit() #dont remove this, in case hand processing was cancelled. self.fdb.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
except (fpdb_simple.FpdbError), fe: except (fpdb_simple.FpdbError), fe:
errors+=1 errors += 1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
#fe.printStackTrace() #todo: get stacktrace #fe.printStackTrace() #todo: get stacktrace
self.fdb.db.rollback() self.fdb.db.rollback()
if (self.settings['failOnError']): if self.settings['failOnError']:
self.fdb.db.commit() #dont remove this, in case hand processing was cancelled. self.fdb.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
if (self.settings['minPrint']!=0):
if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0): if self.settings['minPrint']:
if not ((stored+duplicates+partial+errors) % self.settings['minPrint']):
print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors
if (self.settings['handCount']!=0): if self.settings['handCount']:
if ((stored+duplicates+partial+errors)>=self.settings['handCount']): if ((stored+duplicates+partial+errors) >= self.settings['handCount']):
if (not self.settings['quiet']): if not self.settings['quiet']:
print "quitting due to reaching the amount of hands to be imported" print "quitting due to reaching the amount of hands to be imported"
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime) print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime)
sys.exit(0) sys.exit(0)
startpos=endpos startpos = endpos
ttime = time() - starttime ttime = time() - starttime
print "\rTotal stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", ttime print "\rTotal stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", ttime
if stored==0: if not stored:
if duplicates>0: if duplicates:
for line_no in range(len(self.lines)): for line_no in xrange(len(self.lines)):
if self.lines[line_no].find("Game #")!=-1: if self.lines[line_no].find("Game #")!=-1:
final_game_line=self.lines[line_no] final_game_line=self.lines[line_no]
handsId=fpdb_simple.parseSiteHandNo(final_game_line) handsId=fpdb_simple.parseSiteHandNo(final_game_line)

View File

@ -64,7 +64,7 @@ def mainParser(backend, db, cursor, site, category, hand, config):
fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo) fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo)
hand=fpdb_simple.filterCrap(site, hand, isTourney) hand = fpdb_simple.filterCrap(site, hand, isTourney)
#part 2: classify lines by type (e.g. cards, action, win, sectionchange) and street #part 2: classify lines by type (e.g. cards, action, win, sectionchange) and street
fpdb_simple.classifyLines(hand, category, lineTypes, lineStreets) fpdb_simple.classifyLines(hand, category, lineTypes, lineStreets)

View File

@ -73,7 +73,8 @@ def ring_holdem_omaha(config, backend, db, cursor, base, category, site_hand_no,
saveActions = False if import_options['saveActions'] == 'False' else True saveActions = False if import_options['saveActions'] == 'False' else True
fastStoreHudCache = False if import_options['fastStoreHudCache'] == 'False' else True fastStoreHudCache = False if import_options['fastStoreHudCache'] == 'False' else True
# print "DEBUG: saveActions = '%s' fastStoreHudCache = '%s'"%(saveActions, fastStoreHudCache) # print "DEBUG: saveActions = '%s' fastStoreHudCache = '%s'"%(saveActions, fastStoreHudCache)
# print "DEBUG: import_options = ", import_options
t0 = time() t0 = time()
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)

View File

@ -384,17 +384,16 @@ def getLastInsertId(backend, conn, cursor):
#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):
result=[] return [buyin + fee for i in xrange(count)]
for i in xrange(count):
result.append (buyin+fee)
return result
#end def calcPayin #end def calcPayin
def checkPositions(positions): def checkPositions(positions):
""" verify positions are valid """ """ verify positions are valid """
for p in positions: if any(not (p == "B" or p == "S" or (p >= 0 and p <= 9)) for p in positions):
if not (p == "B" or p == "S" or (p >= 0 and p <= 9)): raise FpdbError("invalid position '"+p+"' found in checkPositions")
raise FpdbError("invalid position '" + p + "' found in checkPositions") # for p in positions:
# if not (p == "B" or p == "S" or (p >= 0 and p <= 9)):
# raise FpdbError("invalid position '" + p + "' found in checkPositions")
### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB ### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB
### eric - position 8 could be valid - if only one blind is posted, but there's still 10 people, ie a sitout is present, and the small is dead... ### eric - position 8 could be valid - if only one blind is posted, but there's still 10 people, ie a sitout is present, and the small is dead...
@ -497,12 +496,13 @@ def convertBlindBet(actionTypes, actionAmounts):
blinds=[] blinds=[]
bets=[] bets=[]
for k in xrange(len(actionTypes[i][j])): for k in xrange(len(actionTypes[i][j])):
if (actionTypes[i][j][k]=="blind"): if actionTypes[i][j][k] == "blind":
blinds.append((i,j,k)) blinds.append((i,j,k))
if (len(blinds)>0 and actionTypes[i][j][k]=="bet"): if blinds and actionTypes[i][j][k] == "bet":
# if (len(blinds)>0 and actionTypes[i][j][k]=="bet"):
bets.append((i,j,k)) bets.append((i,j,k))
if (len(bets)==1): if len(bets) == 1:
blind_amount=actionAmounts[blinds[0][0]][blinds[0][1]][blinds[0][2]] blind_amount=actionAmounts[blinds[0][0]][blinds[0][1]][blinds[0][2]]
bet_amount=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]] bet_amount=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]
actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]=bet_amount-blind_amount actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]=bet_amount-blind_amount
@ -514,7 +514,8 @@ def convertCardValues(arr):
map(convertCardValuesBoard, arr) map(convertCardValuesBoard, arr)
#end def convertCardValues #end def convertCardValues
card_map = { "2": 2, "3" : 3, "4" : 4, "5" : 5, "6" : 6, "7" : 7, "8" : 8, "9" : 9, "T" : 10, "J" : 11, "Q" : 12, "K" : 13, "A" : 14} # a 0-card is one in a stud game that we did not see or was not shown
card_map = { 0: 0, "2": 2, "3" : 3, "4" : 4, "5" : 5, "6" : 6, "7" : 7, "8" : 8, "9" : 9, "T" : 10, "J" : 11, "Q" : 12, "K" : 13, "A" : 14}
#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details #converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details
def convertCardValuesBoard(arr): def convertCardValuesBoard(arr):
@ -612,88 +613,82 @@ def filterAnteBlindFold(site,hand):
if foldeeName in line: if foldeeName in line:
hand[i] = None hand[i] = None
hand = [line for line in hand if line] return [line for line in hand if line]
#end def filterAnteFold #end def filterAnteFold
def stripEOLspaces(str): def stripEOLspaces(str):
if str[-1] == ' ': return str.rstrip()
str = str[:-1]
if str[-1] == ' ':
str = str[:-1]
return str
#removes useless lines as well as trailing spaces #removes useless lines as well as trailing spaces
def filterCrap(site, hand, isTourney): def filterCrap(site, hand, isTourney):
#remove two trailing spaces at end of line #remove two trailing spaces at end of line
hand = [stripEOLspaces(line) for line in hand] hand = [line.rstrip() for line in hand]
#print "hand after trailing space removal in filterCrap:",hand #print "hand after trailing space removal in filterCrap:",hand
#general variable position word filter/string filter #general variable position word filter/string filter
for i in xrange (len(hand)): for i in xrange (len(hand)):
if (hand[i].startswith("Board [")): if hand[i].startswith("Board ["):
hand[i] = False hand[i] = False
elif (hand[i].find(" out of hand ")!=-1): elif hand[i].find(" out of hand ")!=-1:
hand[i]=hand[i][:-56] hand[i]=hand[i][:-56]
elif (hand[i].find("($0 in chips)") != -1): elif "($0 in chips)" in hand[i]:
hand[i] = False hand[i] = False
elif (hand[i]=="*** HOLE CARDS ***"): elif hand[i]=="*** HOLE CARDS ***":
hand[i] = False hand[i] = False
elif (hand[i].endswith("has been disconnected")): elif hand[i].endswith("has been disconnected"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("has requested TIME")): elif hand[i].endswith("has requested TIME"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("has returned")): elif hand[i].endswith("has returned"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("will be allowed to play after the button")): elif hand[i].endswith("will be allowed to play after the button"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("has timed out")): elif hand[i].endswith("has timed out"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("has timed out while disconnected")): elif hand[i].endswith("has timed out while disconnected"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("has timed out while being disconnected")): elif hand[i].endswith("has timed out while being disconnected"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("is connected")): elif hand[i].endswith("is connected"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("is disconnected")): elif hand[i].endswith("is disconnected"):
hand[i] = False hand[i] = False
elif (hand[i].endswith(" is feeling angry")): elif hand[i].endswith(" is feeling angry"):
hand[i] = False hand[i] = False
elif (hand[i].endswith(" is feeling confused")): elif hand[i].endswith(" is feeling confused"):
hand[i] = False hand[i] = False
elif (hand[i].endswith(" is feeling happy")): elif hand[i].endswith(" is feeling happy"):
hand[i] = False hand[i] = False
elif (hand[i].endswith(" is feeling normal")): elif hand[i].endswith(" is feeling normal"):
hand[i] = False hand[i] = False
elif (hand[i].find(" is low with [")!=-1): elif " is low with [" in hand[i]:
hand[i] = False hand[i] = False
#elif (hand[i].find("-max Seat #")!=-1 and hand[i].find(" is the button")!=-1): #elif (hand[i].find("-max Seat #")!=-1 and hand[i].find(" is the button")!=-1):
# toRemove.append(hand[i]) # toRemove.append(hand[i])
elif (hand[i].endswith(" mucks")): elif hand[i].endswith(" mucks"):
hand[i] = False hand[i] = False
elif (hand[i].endswith(": mucks hand")): elif hand[i].endswith(": mucks hand"):
hand[i] = False hand[i] = False
elif (hand[i]=="No low hand qualified"): elif hand[i] == "No low hand qualified":
hand[i] = False hand[i] = False
elif (hand[i]=="Pair on board - a double bet is allowed"): elif hand[i] == "Pair on board - a double bet is allowed":
hand[i] = False hand[i] = False
elif (hand[i].find(" shows ")!=-1 and hand[i].find("[")==-1): elif " shows " in hand[i] and "[" not in hand[i]:
hand[i] = False hand[i] = False
#elif (hand[i].startswith("Table '") and hand[i].endswith("-max")): elif hand[i].startswith("The button is in seat #"):
# toRemove.append(hand[i])
elif (hand[i].startswith("The button is in seat #")):
hand[i] = False hand[i] = False
#above is alphabetic, reorder below if bored #above is alphabetic, reorder below if bored
elif (hand[i].startswith("Time has expired")): elif hand[i].startswith("Time has expired"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("has reconnected")): elif hand[i].endswith("has reconnected"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("seconds left to act")): elif hand[i].endswith("seconds left to act"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("seconds to reconnect")): elif hand[i].endswith("seconds to reconnect"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("was removed from the table for failing to post")): elif hand[i].endswith("was removed from the table for failing to post"):
hand[i] = False hand[i] = False
elif (hand[i].find("joins the table at seat ")!=-1): elif "joins the table at seat " in hand[i]:
hand[i] = False hand[i] = False
elif (hand[i].endswith(" sits down")): elif (hand[i].endswith(" sits down")):
hand[i] = False hand[i] = False
@ -701,20 +696,20 @@ def filterCrap(site, hand, isTourney):
hand[i] = False hand[i] = False
elif (hand[i].endswith(" stands up")): elif (hand[i].endswith(" stands up")):
hand[i] = False hand[i] = False
elif (hand[i].find("is high with ")!=-1): elif "is high with" in hand[i]:
hand[i] = False hand[i] = False
elif (hand[i].endswith("doesn't show hand")): elif hand[i].endswith("doesn't show hand"):
hand[i] = False hand[i] = False
elif (hand[i].endswith("is being treated as all-in")): elif hand[i].endswith("is being treated as all-in"):
hand[i] = False hand[i] = False
elif (hand[i].find(" adds $")!=-1): elif " adds $" in hand[i]:
hand[i] = False hand[i] = False
elif (hand[i]=="Betting is capped"): elif hand[i] == "Betting is capped":
hand[i] = False hand[i] = False
#site specific variable position filter #site specific variable position filter
elif (hand[i].find(" said, \"")!=-1): elif 'said, "' in hand[i]:
hand[i] = False hand[i] = False
elif (hand[i].find(": ")!=-1 and site=="ftp" and hand[i].find("Seat ")==-1 and hand[i].find(": Table")==-1): #filter ftp chat elif site == "ftp" and ":" in hand[i] and "Seat " not in hand[i] and ": Table" not in hand[i]: # FTP chat
hand[i] = False hand[i] = False
if isTourney and not hand[i] == False: if isTourney and not hand[i] == False:
if (hand[i].endswith(" is sitting out") and (not hand[i].startswith("Seat "))): if (hand[i].endswith(" is sitting out") and (not hand[i].startswith("Seat "))):
@ -732,25 +727,25 @@ def filterCrap(site, hand, isTourney):
#end filterCrap #end filterCrap
#takes a poker float (including , for thousand seperator and converts it to an int #takes a poker float (including , for thousand seperator and converts it to an int
def float2int (string): def float2int(string):
pos=string.find(",") pos = string.find(",")
if (pos!=-1): #remove , the thousand seperator if pos != -1: #remove , the thousand seperator
string = "%s%s" % (string[0:pos], string[pos+1:]) string = "%s%s" % (string[0:pos], string[pos+1:])
pos=string.find(".") pos = string.find(".")
if (pos!=-1): #remove decimal point if pos != -1: #remove decimal point
string = "%s%s" % (string[0:pos], string[pos+1:]) string = "%s%s" % (string[0:pos], string[pos+1:])
result = int(string) result = int(string)
if pos == -1: #no decimal point - was in full dollars - need to multiply with 100 if pos == -1: #no decimal point - was in full dollars - need to multiply with 100
result*=100 result *= 100
return result return result
#end def float2int #end def float2int
ActionLines = ( "calls $", ": calls ", "brings in for", "completes it to", "posts small blind", ActionLines = ( "calls $", ": calls ", "brings in for", "completes it to", "posts small blind",
"posts the small blind", "posts big blind", "posts the big blind", "posts the small blind", "posts big blind", "posts the big blind",
"posts small & big blinds", "posts $", "posts a dead", "bets $", "posts small & big blinds", "posts $", "posts a dead", "bets $",
": bets ", "raises") ": bets ", " raises")
#returns boolean whether the passed line is an action line #returns boolean whether the passed line is an action line
def isActionLine(line): def isActionLine(line):
@ -761,7 +756,8 @@ def isActionLine(line):
elif (line.startswith("Uncalled bet")): elif (line.startswith("Uncalled bet")):
return True return True
return len( [ x for x in ActionLines if x in line]) > 0 return any(x for x in ActionLines if x in line)
# return bool([ x for x in ActionLines if x in line])
# ret = any(True for searchstr in ActionLines if searchstr in line) # ret = any(True for searchstr in ActionLines if searchstr in line)
# ret = len( [ x for x in ActionLines if line.find(x) > -1] ) > 0 # ret = len( [ x for x in ActionLines if line.find(x) > -1] ) > 0
# ret = any(searchstr in line for searchstr in ActionLines) # ret = any(searchstr in line for searchstr in ActionLines)
@ -787,10 +783,11 @@ def isTourney(topline):
#end def isTourney #end def isTourney
WinLines = ( "wins the pot", "ties for the ", "wins side pot", "wins the low main pot", "wins the high main pot", WinLines = ( "wins the pot", "ties for the ", "wins side pot", "wins the low main pot", "wins the high main pot",
"wins the low",
"wins the high pot", "wins the high side pot", "wins the main pot", "wins the side pot", "collected" ) "wins the high pot", "wins the high side pot", "wins the main pot", "wins the side pot", "collected" )
#returns boolean whether the passed line is a win line #returns boolean whether the passed line is a win line
def isWinLine(line): def isWinLine(line):
return len( [ x for x in WinLines if x in line ] ) > 0 return any(x for x in WinLines if x in line)
#end def isWinLine #end def isWinLine
#returns the amount of cash/chips put into the put in the given action line #returns the amount of cash/chips put into the put in the given action line
@ -883,41 +880,24 @@ def goesAllInOnThisLine(line):
#end def goesAllInOnThisLine #end def goesAllInOnThisLine
#returns the action type code (see table design) of the given action line #returns the action type code (see table design) of the given action line
ActionTypes = { 'calls':"call", 'brings in for':"blind", 'completes it to':"bet", ' posts $':"blind",
' posts a dead ' : "blind", ' posts the small blind of $':"blind", ': posts big blind ':"blind",
' posts the big blind of $':"blind", ': posts small & big blinds $':"blind",
': posts small blind $':"blind",
' bets' : "bet", ' raises' : "bet"
}
def parseActionType(line): def parseActionType(line):
if (line.startswith("Uncalled bet")): if (line.startswith("Uncalled bet")):
return "unbet" return "unbet"
elif (line.endswith("folds")): elif (line.endswith(" folds")):
return "fold" return "fold"
elif (line.endswith("checks")): elif (line.endswith(" checks")):
return "check" return "check"
elif (line.find("calls")!=-1):
return "call"
elif (line.find("brings in for")!=-1):
return "blind"
elif (line.find("completes it to")!=-1):
return "bet"
#todo: what if someone completes instead of bringing in?
elif (line.find(" posts $")!=-1):
return "blind"
elif (line.find(" posts a dead ")!=-1):
return "blind"
elif (line.find(": posts small blind ")!=-1):
return "blind"
elif (line.find(" posts the small blind of $")!=-1):
return "blind"
elif (line.find(": posts big blind ")!=-1):
return "blind"
elif (line.find(" posts the big blind of $")!=-1):
return "blind"
elif (line.find(": posts small & big blinds $")!=-1):
return "blind"
#todo: seperately record voluntary blind payments made to join table out of turn
elif (line.find("bets")!=-1):
return "bet"
elif (line.find("raises")!=-1):
return "bet"
else: else:
raise FpdbError ("failed to recognise actiontype in parseActionLine in: "+line) for x in ActionTypes:
if x in line:
return ActionTypes[x]
raise FpdbError ("failed to recognise actiontype in parseActionLine in: "+line)
#end def parseActionType #end def parseActionType
#parses the ante out of the given line and checks which player paid it, updates antes accordingly. #parses the ante out of the given line and checks which player paid it, updates antes accordingly.
@ -1052,12 +1032,12 @@ def parseFee(topline):
def parseHandStartTime(topline, site): def parseHandStartTime(topline, site):
#convert x:13:35 to 0x:13:35 #convert x:13:35 to 0x:13:35
counter=0 counter=0
while (True): while counter < 10:
pos=topline.find(" "+str(counter)+":") pos = topline.find(" %d:" % counter)
if (pos!=-1): if pos != -1:
topline=topline[0:pos+1]+"0"+topline[pos+1:] topline = "%s0%s" % (topline[0:pos+1], topline[pos+1:])
counter+=1 break
if counter==10: break counter += 1
isUTC=False isUTC=False
if site=="ftp": if site=="ftp":
@ -1101,64 +1081,58 @@ def parseHandStartTime(topline, site):
#end def parseHandStartTime #end def parseHandStartTime
#parses the names out of the given lines and returns them as an array #parses the names out of the given lines and returns them as an array
def findName(line):
pos1 = line.find(":") + 2
pos2 = line.rfind("(") - 1
return unicode(line[pos1:pos2], "latin-1")
def parseNames(lines): def parseNames(lines):
result = [] return [findName(line) for line in lines]
for i in xrange (len(lines)):
pos1=lines[i].find(":")+2
pos2=lines[i].rfind("(")-1
tmp=lines[i][pos1:pos2]
#print "parseNames, tmp original:",tmp
tmp=unicode(tmp,"latin-1")
#print "parseNames, tmp after unicode latin-1 conversion:",tmp
result.append(tmp)
return result
#end def parseNames #end def parseNames
def parsePositions(hand, names): def parsePositions(hand, names):
positions = map(lambda x: -1, names) positions = [-1 for i in names]
sb, bb = -1, -1
#find blinds #find blinds
sb,bb=-1,-1 for line in hand:
for i in xrange (len(hand)): if sb == -1 and "small blind" in line and "dead small blind" not in line:
if (sb==-1 and hand[i].find("small blind")!=-1 and hand[i].find("dead small blind")==-1): sb = line
sb=hand[i] if bb == -1 and "big blind" in line and "dead big blind" not in line:
#print "sb:",sb bb = line
if (bb==-1 and hand[i].find("big blind")!=-1 and hand[i].find("dead big blind")==-1):
bb=hand[i]
#print "bb:",bb
#identify blinds #identify blinds
#print "parsePositions before recognising sb/bb. names:",names #print "parsePositions before recognising sb/bb. names:",names
sbExists=True sbExists = True
if (sb!=-1): if sb != -1:
sb=recognisePlayerNo(sb, names, "bet") sb = recognisePlayerNo(sb, names, "bet")
else: else:
sbExists=False sbExists = False
if (bb!=-1): if bb != -1:
bb=recognisePlayerNo(bb, names, "bet") bb = recognisePlayerNo(bb, names, "bet")
# print "sb = ", sb, "bb = ", bb # print "sb = ", sb, "bb = ", bb
if bb == sb: if bb == sb: # if big and small are same, then don't duplicate the small
sbExists = False sbExists = False
sb = -1 sb = -1
#write blinds into array #write blinds into array
if (sbExists): if sbExists:
positions[sb]="S" positions[sb]="S"
positions[bb]="B" positions[bb]="B"
#fill up rest of array #fill up rest of array
if (sbExists): if sbExists:
arraypos=sb-1 arraypos = sb-1
else: else:
arraypos=bb-1 arraypos = bb-1
distFromBtn=0 distFromBtn=0
while (arraypos>=0 and arraypos != bb): while arraypos >= 0 and arraypos != bb:
#print "parsePositions first while, arraypos:",arraypos,"positions:",positions #print "parsePositions first while, arraypos:",arraypos,"positions:",positions
positions[arraypos]=distFromBtn positions[arraypos] = distFromBtn
arraypos-=1 arraypos -= 1
distFromBtn+=1 distFromBtn += 1
# eric - this takes into account dead seats between blinds # eric - this takes into account dead seats between blinds
if sbExists: if sbExists:
@ -1182,11 +1156,10 @@ def parsePositions(hand, names):
arraypos-=1 arraypos-=1
distFromBtn+=1 distFromBtn+=1
for i in xrange (len(names)): if any(p == -1 for p in positions):
if positions[i]==-1: print "parsePositions names:",names
print "parsePositions names:",names print "result:",positions
print "result:",positions raise FpdbError ("failed to read positions")
raise FpdbError ("failed to read positions")
# print str(positions), "\n" # print str(positions), "\n"
return positions return positions
#end def parsePositions #end def parsePositions
@ -1254,22 +1227,23 @@ def parseTourneyNo(topline):
#parses a win/collect line. manipulates the passed array winnings, no explicit return #parses a win/collect line. manipulates the passed array winnings, no explicit return
def parseWinLine(line, site, names, winnings, isTourney): def parseWinLine(line, site, names, winnings, isTourney):
#print "parseWinLine: line:",line #print "parseWinLine: line:",line
for i in xrange(len(names)): for i,n in enumerate(names):
if (line.startswith(names[i].encode("latin-1"))): #found a winner n = n.encode("latin-1")
if line.startswith(n):
if isTourney: if isTourney:
pos1=line.rfind("collected ")+10 pos1 = line.rfind("collected ") + 10
if (site=="ftp"): if site == "ftp":
pos2=line.find(")", pos1) pos2 = line.find(")", pos1)
elif (site=="ps"): elif site == "ps":
pos2=line.find(" ", pos1) pos2 = line.find(" ", pos1)
winnings[i]+=int(line[pos1:pos2]) winnings[i] += int(line[pos1:pos2])
else: else:
pos1=line.rfind("$")+1 pos1 = line.rfind("$") + 1
if (site=="ftp"): if site == "ftp":
pos2=line.find(")", pos1) pos2 = line.find(")", pos1)
elif (site=="ps"): elif site == "ps":
pos2=line.find(" ", pos1) pos2 = line.find(" ", pos1)
winnings[i]+=float2int(line[pos1:pos2]) winnings[i] += float2int(line[pos1:pos2])
#end def parseWinLine #end def parseWinLine
#returns the category (as per database) string for the given line #returns the category (as per database) string for the given line
@ -1414,21 +1388,51 @@ def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon):
#end def recogniseTourneyTypeId #end def recogniseTourneyTypeId
#returns the SQL ids of the names given in an array #returns the SQL ids of the names given in an array
# TODO: if someone gets industrious, they should make the parts that use the output of this function deal with a dict
# { playername: id } instead of depending on it's relation to the positions list
# then this can be reduced in complexity a bit
def recognisePlayerIDs(cursor, names, site_id): def recognisePlayerIDs(cursor, names, site_id):
result = [] q = "SELECT name,id FROM Players WHERE name=%s" % " OR name=".join(["%s" for n in names])
for i in xrange(len(names)): cursor.execute(q, names) # get all playerids by the names passed in
cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) ids = dict(cursor.fetchall()) # convert to dict
tmp=cursor.fetchall() if len(ids) != len(names):
if (len(tmp)==0): #new player notfound = [n for n in names if n not in ids] # make list of names not in database
cursor.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (names[i], site_id)) if notfound: # insert them into database
#print "Number of players rows inserted: %d" % cursor.rowcount cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound))
cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) q2 = "SELECT name,id FROM Players WHERE name=%s" % " OR name=".join(["%s" for n in notfound])
tmp=cursor.fetchall() cursor.execute(q2, notfound) # get their new ids
#print "recognisePlayerIDs, names[i]:",names[i],"tmp:",tmp tmp = dict(cursor.fetchall())
result.append(tmp[0][0]) for n in tmp: # put them all into the same dict
return result ids[n] = tmp[n]
# return them in the SAME ORDER that they came in in the names argument, rather than the order they came out of the DB
return [ids[n] for n in names]
#end def recognisePlayerIDs #end def recognisePlayerIDs
# Here's a version that would work if it wasn't for the fact that it needs to have the output in the same order as input
# this version could also be improved upon using list comprehensions, etc
#def recognisePlayerIDs(cursor, names, site_id):
# result = []
# notfound = []
# cursor.execute("SELECT name,id FROM Players WHERE name='%s'" % "' OR name='".join(names))
# tmp = dict(cursor.fetchall())
# for n in names:
# if n not in tmp:
# notfound.append(n)
# else:
# result.append(tmp[n])
# if notfound:
# cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound))
# cursor.execute("SELECT id FROM Players WHERE name='%s'" % "' OR name='".join(notfound))
# tmp = cursor.fetchall()
# for n in tmp:
# result.append(n[0])
#
# return result
#recognises the name in the given line and returns its array position in the given array #recognises the name in the given line and returns its array position in the given array
def recognisePlayerNo(line, names, atype): def recognisePlayerNo(line, names, atype):
#print "recogniseplayerno, names:",names #print "recogniseplayerno, names:",names
@ -2262,7 +2266,7 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
except TypeError: except TypeError:
row=[] row=[]
if (len(row)==0): if not row:
#print "new huddata row" #print "new huddata row"
doInsert=True doInsert=True
row=[] row=[]
@ -2275,9 +2279,11 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
else: else:
doInsert=False doInsert=False
# This is making a copy of the original list, although i really don't see any reason it's being done?
newrow=[] newrow=[]
for i in xrange(len(row)): newrow.extend(row)
newrow.append(row[i]) # for i in xrange(len(row)):
# newrow.append(row[i])
row=newrow row=newrow
if base=="hold": if base=="hold":

View File

@ -35,6 +35,9 @@ def testGameInfo():
(u"PokerStars Game #25923772706: Badugi Limit ($0.25/$0.50) - 2009/03/13 16:40:58 ET", (u"PokerStars Game #25923772706: Badugi Limit ($0.25/$0.50) - 2009/03/13 16:40:58 ET",
{'type':'ring', 'base':'draw', 'category':'badugi', 'limitType':'fl', 'sb':'0.25', 'bb':'0.50','currency':'USD'}), {'type':'ring', 'base':'draw', 'category':'badugi', 'limitType':'fl', 'sb':'0.25', 'bb':'0.50','currency':'USD'}),
(u"PokerStars Game #22073591924: Hold'em No Limit ($0.50/$1.00) - 2008/11/16 1:22:21 CET [2008/11/15 19:22:21 ET]",
{'type':'ring', 'base':'hold', 'category':'holdem', 'limitType':'nl', 'sb':'0.50', 'bb':'1.00','currency':'USD'}),
(u"PokerStars Game #25974627364: Omaha Pot Limit ($0.05/$0.10) - 2009/03/15 0:29:00 ET", (u"PokerStars Game #25974627364: Omaha Pot Limit ($0.05/$0.10) - 2009/03/15 0:29:00 ET",
{'type':'ring', 'base':'hold', 'category':'omahahi', 'limitType':'pl', 'sb':'0.05', 'bb':'0.10','currency':'USD'}) {'type':'ring', 'base':'hold', 'category':'omahahi', 'limitType':'pl', 'sb':'0.05', 'bb':'0.10','currency':'USD'})
) )