Merge branch 'master' of git://git.assembla.com/free_poker_tools
This commit is contained in:
commit
959e0e11cf
2
Makefile
2
Makefile
|
@ -1,5 +1,5 @@
|
|||
# Variable definitions
|
||||
VERSION = 0.12
|
||||
VERSION = 0.20
|
||||
DATE = $(shell date +%Y%m%d)
|
||||
|
||||
all:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
README.txt
|
||||
updated 26 March 2009, REB
|
||||
updated 22 February 2010, REB
|
||||
|
||||
fpdb - Free Poker Database
|
||||
|
||||
|
@ -29,7 +29,7 @@ fpdb supports:
|
|||
Omaha (incl Hi/low)
|
||||
7 Card Stud (incl Hi/low)
|
||||
Razz
|
||||
Draw support is under development
|
||||
Triple Draw and Badugi
|
||||
Mixed Games -- HUD under development
|
||||
|
||||
Operating Systems:
|
||||
|
@ -38,23 +38,38 @@ fpdb supports:
|
|||
Mac OS/X -- no support for HUD
|
||||
|
||||
Databases:
|
||||
SQLite configured by default
|
||||
MySQL
|
||||
PostgreSQL
|
||||
SQLite under development
|
||||
|
||||
Downloads:
|
||||
Releases: http://sourceforge.net/project/showfiles.php?group_id=226872
|
||||
Development code via git: http://www.assembla.com/spaces/free_poker_tools/trac_git_tool
|
||||
|
||||
Developers:
|
||||
At least 7 people have contributed code or patches. Others are welcome.
|
||||
At least 10 people have contributed code or patches. Others are welcome.
|
||||
|
||||
Source Code:
|
||||
If you received fpdb as the Windows compressed exe, then you did not
|
||||
receive souce code for fpdb or the included libraries. If you wish, you can
|
||||
obtain the source code here:
|
||||
|
||||
fpdb: see Downloads, above.
|
||||
python: http://python.org/
|
||||
gtk: http://www.gtk.org/download.html
|
||||
pygtk: http://www.pygtk.org/downloads.html
|
||||
psycopg2: http://initd.org/pub/software/psycopg/
|
||||
mysqldb: http://sourceforge.net/projects/mysql-python/files/
|
||||
sqlalchemy: http://www.sqlalchemy.org/download.html
|
||||
numpy: http://www.scipy.org/Download
|
||||
matplotlib: http://sourceforge.net/projects/matplotlib/files/
|
||||
|
||||
License
|
||||
=======
|
||||
Trademarks of third parties have been used under Fair Use or similar laws.
|
||||
|
||||
Copyright 2008 Steffen Jobbagy-Felso
|
||||
Copyright 2009 Ray E. Barker
|
||||
Copyright 2009,2010 Ray E. Barker
|
||||
Permission is granted to copy, distribute and/or modify this
|
||||
document under the terms of the GNU Free Documentation License,
|
||||
Version 1.2 as published by the Free Software Foundation; with
|
||||
|
|
|
@ -414,6 +414,7 @@ class Import:
|
|||
self.hhArchiveBase = node.getAttribute("hhArchiveBase")
|
||||
self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=True)
|
||||
self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
|
||||
self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False)
|
||||
|
||||
def __str__(self):
|
||||
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
|
||||
|
@ -469,7 +470,8 @@ class Config:
|
|||
|
||||
self.file = file
|
||||
self.dir_self = get_exec_path()
|
||||
self.dir_config = os.path.dirname(self.file)
|
||||
# self.dir_config = os.path.dirname(self.file)
|
||||
self.dir_config = get_default_config_path()
|
||||
self.dir_log = os.path.join(self.dir_config, 'log')
|
||||
self.dir_database = os.path.join(self.dir_config, 'database')
|
||||
self.log_file = os.path.join(self.dir_log, 'fpdb-log.txt')
|
||||
|
@ -481,12 +483,19 @@ class Config:
|
|||
print "\nReading configuration file %s\n" % file
|
||||
try:
|
||||
doc = xml.dom.minidom.parse(file)
|
||||
self.file_error = None
|
||||
except:
|
||||
log.error("Error parsing %s. See error log file." % (file))
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
print "press enter to continue"
|
||||
sys.stdin.readline()
|
||||
sys.exit()
|
||||
self.file_error = sys.exc_info()[1]
|
||||
# we could add a parameter to decide whether to return or read a line and exit?
|
||||
return
|
||||
#print "press enter to continue"
|
||||
#sys.stdin.readline()
|
||||
#sys.exit()
|
||||
#ExpatError: not well-formed (invalid token): line 511, column 4
|
||||
#sys.exc_info = (<class 'xml.parsers.expat.ExpatError'>, ExpatError('not well-formed (invalid token): line 511,
|
||||
# column 4',), <traceback object at 0x024503A0>)
|
||||
|
||||
self.doc = doc
|
||||
self.supported_sites = {}
|
||||
|
@ -688,18 +697,8 @@ class Config:
|
|||
try: db['db-server'] = self.supported_databases[name].db_server
|
||||
except: pass
|
||||
|
||||
if self.supported_databases[name].db_server== DATABASE_TYPE_MYSQL:
|
||||
db['db-backend'] = 2
|
||||
elif self.supported_databases[name].db_server== DATABASE_TYPE_POSTGRESQL:
|
||||
db['db-backend'] = 3
|
||||
elif self.supported_databases[name].db_server== DATABASE_TYPE_SQLITE:
|
||||
db['db-backend'] = 4
|
||||
# sqlcoder: this assignment fixes unicode problems for me with sqlite (windows, cp1252)
|
||||
# feel free to remove or improve this if you understand the problems
|
||||
# better than me (not hard!)
|
||||
Charset.not_needed1, Charset.not_needed2, Charset.not_needed3 = True, True, True
|
||||
else:
|
||||
raise ValueError('Unsupported database backend: %s' % self.supported_databases[name].db_server)
|
||||
db['db-backend'] = self.get_backend(self.supported_databases[name].db_server)
|
||||
|
||||
return db
|
||||
|
||||
def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
|
||||
|
@ -719,6 +718,23 @@ class Config:
|
|||
if db_type is not None: self.supported_databases[db_name].dp_type = db_type
|
||||
return
|
||||
|
||||
def get_backend(self, name):
|
||||
"""Returns the number of the currently used backend"""
|
||||
if name == DATABASE_TYPE_MYSQL:
|
||||
ret = 2
|
||||
elif name == DATABASE_TYPE_POSTGRESQL:
|
||||
ret = 3
|
||||
elif name == DATABASE_TYPE_SQLITE:
|
||||
ret = 4
|
||||
# sqlcoder: this assignment fixes unicode problems for me with sqlite (windows, cp1252)
|
||||
# feel free to remove or improve this if you understand the problems
|
||||
# better than me (not hard!)
|
||||
Charset.not_needed1, Charset.not_needed2, Charset.not_needed3 = True, True, True
|
||||
else:
|
||||
raise ValueError('Unsupported database backend: %s' % self.supported_databases[name].db_server)
|
||||
|
||||
return ret
|
||||
|
||||
def getDefaultSite(self):
|
||||
"Returns first enabled site or None"
|
||||
for site_name,site in self.supported_sites.iteritems():
|
||||
|
@ -808,8 +824,12 @@ class Config:
|
|||
try: imp['saveActions'] = self.imp.saveActions
|
||||
except: imp['saveActions'] = True
|
||||
|
||||
try: imp['saveStarsHH'] = self.imp.saveStarsHH
|
||||
except: imp['saveStarsHH'] = False
|
||||
|
||||
try: imp['fastStoreHudCache'] = self.imp.fastStoreHudCache
|
||||
except: imp['fastStoreHudCache'] = True
|
||||
|
||||
return imp
|
||||
|
||||
def get_default_paths(self, site = None):
|
||||
|
|
|
@ -142,14 +142,18 @@ class Database:
|
|||
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
||||
]
|
||||
, [ # indexes for sqlite (list index 4)
|
||||
# {'tab':'Players', 'col':'name', 'drop':0} unique indexes not dropped
|
||||
# {'tab':'Hands', 'col':'siteHandNo', 'drop':0} unique indexes not dropped
|
||||
{'tab':'Hands', 'col':'gametypeId', 'drop':0}
|
||||
, {'tab':'HandsPlayers', 'col':'handId', 'drop':0}
|
||||
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':0}
|
||||
, {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0}
|
||||
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
|
||||
#, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} unique indexes not dropped
|
||||
, {'tab':'HudCache', 'col':'gametypeId', 'drop':1}
|
||||
, {'tab':'HudCache', 'col':'playerId', 'drop':0}
|
||||
, {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0}
|
||||
, {'tab':'Players', 'col':'siteId', 'drop':1}
|
||||
, {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1}
|
||||
, {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0}
|
||||
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -226,7 +230,7 @@ class Database:
|
|||
# create index indexname on tablename (col);
|
||||
|
||||
|
||||
def __init__(self, c, sql = None):
|
||||
def __init__(self, c, sql = None, autoconnect = True):
|
||||
#log = Configuration.get_logger("logging.conf", "db", log_dir=c.dir_log)
|
||||
log.debug("Creating Database instance, sql = %s" % sql)
|
||||
self.config = c
|
||||
|
@ -247,6 +251,7 @@ class Database:
|
|||
else:
|
||||
self.sql = sql
|
||||
|
||||
if autoconnect:
|
||||
# connect to db
|
||||
self.do_connect(c)
|
||||
|
||||
|
@ -313,7 +318,7 @@ class Database:
|
|||
self.__connected = True
|
||||
|
||||
def connect(self, backend=None, host=None, database=None,
|
||||
user=None, password=None):
|
||||
user=None, password=None, create=False):
|
||||
"""Connects a database with the given parameters"""
|
||||
if backend is None:
|
||||
raise FpdbError('Database backend not defined')
|
||||
|
@ -377,6 +382,7 @@ class Database:
|
|||
print msg
|
||||
raise FpdbError(msg)
|
||||
elif backend == Database.SQLITE:
|
||||
create = True
|
||||
import sqlite3
|
||||
if use_pool:
|
||||
sqlite3 = pool.manage(sqlite3, pool_size=1)
|
||||
|
@ -384,13 +390,14 @@ class Database:
|
|||
# log.warning("SQLite won't work well without 'sqlalchemy' installed.")
|
||||
|
||||
if database != ":memory:":
|
||||
if not os.path.isdir(self.config.dir_database):
|
||||
if not os.path.isdir(self.config.dir_database) and create:
|
||||
print "Creating directory: '%s'" % (self.config.dir_database)
|
||||
log.info("Creating directory: '%s'" % (self.config.dir_database))
|
||||
os.mkdir(self.config.dir_database)
|
||||
database = os.path.join(self.config.dir_database, database)
|
||||
self.db_path = database
|
||||
log.info("Connecting to SQLite: %(database)s" % {'database':self.db_path})
|
||||
if os.path.exists(database) or create:
|
||||
self.connection = sqlite3.connect(self.db_path, detect_types=sqlite3.PARSE_DECLTYPES )
|
||||
sqlite3.register_converter("bool", lambda x: bool(int(x)))
|
||||
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
|
||||
|
@ -405,11 +412,13 @@ class Database:
|
|||
self.cursor.execute('PRAGMA temp_store=2') # use memory for temp tables/indexes
|
||||
self.cursor.execute('PRAGMA synchronous=0') # don't wait for file writes to finish
|
||||
else:
|
||||
raise FpdbError("unrecognised database backend:"+backend)
|
||||
raise FpdbError("sqlite database "+database+" does not exist")
|
||||
else:
|
||||
raise FpdbError("unrecognised database backend:"+str(backend))
|
||||
|
||||
self.cursor = self.connection.cursor()
|
||||
self.cursor.execute(self.sql.query['set tx level'])
|
||||
self.check_version(database=database, create=True)
|
||||
self.check_version(database=database, create=create)
|
||||
|
||||
|
||||
def check_version(self, database, create):
|
||||
|
@ -721,6 +730,8 @@ class Database:
|
|||
|
||||
# now get the stats
|
||||
c.execute(self.sql.query[query], subs)
|
||||
#for row in c.fetchall(): # needs "explain query plan" in sql statement
|
||||
# print "query plan: ", row
|
||||
colnames = [desc[0] for desc in c.description]
|
||||
for row in c.fetchall():
|
||||
playerid = row[0]
|
||||
|
@ -907,7 +918,6 @@ class Database:
|
|||
print "warning: constraint %s_%s_fkey not dropped: %s, continuing ..." \
|
||||
% (fk['fktab'],fk['fkcol'], str(sys.exc_value).rstrip('\n'))
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
for idx in self.indexes[self.backend]:
|
||||
|
@ -942,7 +952,6 @@ class Database:
|
|||
print "warning: index %s_%s_idx not dropped %s, continuing ..." \
|
||||
% (idx['tab'],idx['col'], str(sys.exc_value).rstrip('\n'))
|
||||
else:
|
||||
print "Error: Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
if self.backend == self.PGSQL:
|
||||
|
@ -997,7 +1006,6 @@ class Database:
|
|||
except:
|
||||
print " create fk failed: " + str(sys.exc_info())
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
for idx in self.indexes[self.backend]:
|
||||
|
@ -1019,7 +1027,6 @@ class Database:
|
|||
except:
|
||||
print " create index failed: " + str(sys.exc_info())
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
if self.backend == self.PGSQL:
|
||||
|
@ -2096,6 +2103,7 @@ class HandToWrite:
|
|||
|
||||
if __name__=="__main__":
|
||||
c = Configuration.Config()
|
||||
sql = SQL.Sql(db_server = 'sqlite')
|
||||
|
||||
db_connection = Database(c) # mysql fpdb holdem
|
||||
# db_connection = Database(c, 'fpdb-p', 'test') # mysql fpdb holdem
|
||||
|
@ -2113,13 +2121,26 @@ if __name__=="__main__":
|
|||
if hero:
|
||||
print "nutOmatic is id_player = %d" % hero
|
||||
|
||||
# example of displaying query plan in sqlite:
|
||||
if db_connection.backend == 4:
|
||||
print
|
||||
c = db_connection.get_cursor()
|
||||
c.execute('explain query plan '+sql.query['get_table_name'], (h, ))
|
||||
for row in c.fetchall():
|
||||
print "query plan: ", row
|
||||
print
|
||||
|
||||
t0 = time()
|
||||
stat_dict = db_connection.get_stats_from_hand(h, "ring")
|
||||
t1 = time()
|
||||
for p in stat_dict.keys():
|
||||
print p, " ", stat_dict[p]
|
||||
|
||||
print "cards =", db_connection.get_cards(u'1')
|
||||
db_connection.close_connection
|
||||
|
||||
print "get_stats took: %4.3f seconds" % (t1-t0)
|
||||
|
||||
print "press enter to continue"
|
||||
sys.stdin.readline()
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ class DerivedStats():
|
|||
self.handsplayers[player]['wonAtSD'] = 1.0
|
||||
|
||||
for player in hand.pot.committed:
|
||||
self.handsplayers[player]['totalProfit'] = int(self.handsplayers[player]['winnings'] - (100*hand.pot.committed[player]))
|
||||
self.handsplayers[player]['totalProfit'] = int(self.handsplayers[player]['winnings'] - (100*hand.pot.committed[player])- (100*hand.pot.common[player]))
|
||||
|
||||
self.calcCBets(hand)
|
||||
|
||||
|
@ -227,7 +227,7 @@ class DerivedStats():
|
|||
|
||||
#print "bb =", bb, "sb =", sb, "players =", players
|
||||
for i,player in enumerate(reversed(players)):
|
||||
self.handsplayers[player]['position'] = str(i)
|
||||
self.handsplayers[player]['position'] = i
|
||||
|
||||
def assembleHudCache(self, hand):
|
||||
# No real work to be done - HandsPlayers data already contains the correct info
|
||||
|
@ -305,12 +305,13 @@ class DerivedStats():
|
|||
"""Fills stealAttempt(Chance|ed, fold(Bb|Sb)ToSteal(Chance|)
|
||||
|
||||
Steal attempt - open raise on positions 1 0 S - i.e. MP3, CO, BU, SB
|
||||
(note: I don't think PT2 counts SB steals in HU hands, maybe we shouldn't?)
|
||||
Fold to steal - folding blind after steal attemp wo any other callers or raisers
|
||||
"""
|
||||
steal_attempt = False
|
||||
steal_positions = ('1', '0', 'S')
|
||||
steal_positions = (1, 0, 'S')
|
||||
if hand.gametype['base'] == 'stud':
|
||||
steal_positions = ('2', '1', '0')
|
||||
steal_positions = (2, 1, 0)
|
||||
for action in hand.actions[hand.actionStreets[1]]:
|
||||
pname, act = action[0], action[1]
|
||||
posn = self.handsplayers[pname]['position']
|
||||
|
@ -344,9 +345,9 @@ class DerivedStats():
|
|||
for action in hand.actions[hand.actionStreets[1]]:
|
||||
# FIXME: fill other(3|4)BStreet0 - i have no idea what does it mean
|
||||
pname, aggr = action[0], action[1] in ('raises', 'bets')
|
||||
self.handsplayers[pname]['street0_3BChance'] = bet_level == 2
|
||||
self.handsplayers[pname]['street0_3BChance'] = self.handsplayers[pname]['street0_3BChance'] or bet_level == 2
|
||||
self.handsplayers[pname]['street0_4BChance'] = bet_level == 3
|
||||
self.handsplayers[pname]['street0_3BDone'] = aggr and (self.handsplayers[pname]['street0_3BChance'])
|
||||
self.handsplayers[pname]['street0_3BDone'] = self.handsplayers[pname]['street0_3BDone'] or (aggr and self.handsplayers[pname]['street0_3BChance'])
|
||||
self.handsplayers[pname]['street0_4BDone'] = aggr and (self.handsplayers[pname]['street0_4BChance'])
|
||||
if aggr:
|
||||
bet_level += 1
|
||||
|
|
|
@ -211,6 +211,9 @@ class Filters(threading.Thread):
|
|||
self.Button2.connect("clicked", self.callback['button2'], "clicked")
|
||||
self.Button2.set_sensitive(True)
|
||||
|
||||
# make sure any locks on db are released:
|
||||
self.db.rollback()
|
||||
|
||||
def get_vbox(self):
|
||||
"""returns the vbox of this thread"""
|
||||
return self.mainVBox
|
||||
|
|
|
@ -65,8 +65,8 @@ class Fulltilt(HandHistoryConverter):
|
|||
(\s\((?P<TURBO>Turbo)\))?)|(?P<UNREADABLE_INFO>.+))
|
||||
''', re.VERBOSE)
|
||||
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
||||
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{3,15}) \(\$(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
|
||||
re_TourneyPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{3,15}) \(\$?(?P<CASH>[,.0-9]+)\)(, is sitting out)?$', re.MULTILINE)
|
||||
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) \(\$(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
|
||||
re_TourneyPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) \(\$?(?P<CASH>[,.0-9]+)\)(, is sitting out)?$', re.MULTILINE)
|
||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||
|
||||
#static regex for tourney purpose
|
||||
|
@ -128,6 +128,7 @@ class Fulltilt(HandHistoryConverter):
|
|||
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
||||
logging.debug("player_re: " + player_re)
|
||||
self.re_PostSB = re.compile(r"^%s posts the small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||
self.re_PostDead = re.compile(r"^%s posts a dead small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||
self.re_PostBB = re.compile(r"^%s posts (the big blind of )?\$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||
self.re_Antes = re.compile(r"^%s antes \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||
self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
||||
|
@ -298,6 +299,8 @@ class Fulltilt(HandHistoryConverter):
|
|||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||
except: # no small blind
|
||||
hand.addBlind(None, None, None)
|
||||
for a in self.re_PostDead.finditer(hand.handText):
|
||||
hand.addBlind(a.group('PNAME'), 'secondsb', a.group('SB'))
|
||||
for a in self.re_PostBB.finditer(hand.handText):
|
||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||
for a in self.re_PostBoth.finditer(hand.handText):
|
||||
|
|
298
pyfpdb/GuiDatabase.py
Executable file
298
pyfpdb/GuiDatabase.py
Executable file
|
@ -0,0 +1,298 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#Copyright 2008 Carl Gherardi
|
||||
#This program is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU Affero General Public License as published by
|
||||
#the Free Software Foundation, version 3 of the License.
|
||||
#
|
||||
#This program is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU Affero General Public License
|
||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#In the "official" distribution you can find the license in
|
||||
#agpl-3.0.txt in the docs folder of the package.
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import Queue
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import gobject
|
||||
import pango
|
||||
|
||||
import logging
|
||||
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
||||
log = logging.getLogger("maintdbs")
|
||||
|
||||
|
||||
import Exceptions
|
||||
import Database
|
||||
|
||||
|
||||
class GuiDatabase:
|
||||
|
||||
COL_DBMS = 0
|
||||
COL_NAME = 1
|
||||
COL_DESC = 2
|
||||
COL_USER = 3
|
||||
COL_PASS = 4
|
||||
COL_HOST = 5
|
||||
COL_ICON = 6
|
||||
|
||||
def __init__(self, config, mainwin, dia):
|
||||
self.config = config
|
||||
self.main_window = mainwin
|
||||
self.dia = dia
|
||||
|
||||
try:
|
||||
#self.dia.set_modal(True)
|
||||
self.vbox = self.dia.vbox
|
||||
#gtk.Widget.set_size_request(self.vbox, 700, 400);
|
||||
|
||||
# list of databases in self.config.supported_databases:
|
||||
self.liststore = gtk.ListStore(str, str, str, str
|
||||
,str, str, str, str) #object, gtk.gdk.Pixbuf)
|
||||
# dbms, name, comment, user, pass, ip, status(, icon?)
|
||||
# this is how to add a filter:
|
||||
#
|
||||
# # Creation of the filter, from the model
|
||||
# filter = self.liststore.filter_new()
|
||||
# filter.set_visible_column(1)
|
||||
#
|
||||
# # The TreeView gets the filter as model
|
||||
# self.listview = gtk.TreeView(filter)
|
||||
self.listview = gtk.TreeView(model=self.liststore)
|
||||
self.listview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_NONE)
|
||||
self.listcols = []
|
||||
|
||||
scrolledwindow = gtk.ScrolledWindow()
|
||||
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
scrolledwindow.add(self.listview)
|
||||
self.vbox.pack_start(scrolledwindow, expand=True, fill=True, padding=0)
|
||||
|
||||
refreshbutton = gtk.Button("Refresh")
|
||||
refreshbutton.connect("clicked", self.refresh, None)
|
||||
self.vbox.pack_start(refreshbutton, False, False, 3)
|
||||
refreshbutton.show()
|
||||
|
||||
col = self.addTextColumn("Type", 0, False)
|
||||
col = self.addTextColumn("Name", 1, False)
|
||||
col = self.addTextColumn("Description", 2, True)
|
||||
col = self.addTextColumn("Username", 3, True)
|
||||
col = self.addTextColumn("Password", 4, True)
|
||||
col = self.addTextColumn("Host", 5, True)
|
||||
col = self.addTextObjColumn("", 6)
|
||||
|
||||
self.loadDbs()
|
||||
|
||||
self.dia.connect('response', self.dialog_response_cb)
|
||||
except:
|
||||
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||
print 'guidbmaint: '+ err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
|
||||
|
||||
def dialog_response_cb(self, dialog, response_id):
|
||||
# this is called whether close button is pressed or window is closed
|
||||
dialog.destroy()
|
||||
|
||||
|
||||
def get_dialog(self):
|
||||
return self.dia
|
||||
|
||||
def addTextColumn(self, title, n, editable=False):
|
||||
col = gtk.TreeViewColumn(title)
|
||||
self.listview.append_column(col)
|
||||
|
||||
cRender = gtk.CellRendererText()
|
||||
cRender.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
|
||||
cRender.set_property('editable', editable)
|
||||
cRender.connect('edited', self.edited_cb, (self.liststore,n))
|
||||
|
||||
col.pack_start(cRender, True)
|
||||
col.add_attribute(cRender, 'text', n)
|
||||
col.set_max_width(1000)
|
||||
col.set_spacing(0) # no effect
|
||||
self.listcols.append(col)
|
||||
col.set_clickable(True)
|
||||
col.connect("clicked", self.sortCols, n)
|
||||
|
||||
return(col)
|
||||
|
||||
def edited_cb(self, cell, path, new_text, user_data):
|
||||
liststore, col = user_data
|
||||
valid = True
|
||||
name = self.liststore[path][self.COL_NAME]
|
||||
|
||||
# Validate new value (only for dbms so far, but dbms now not updateable so no validation at all!)
|
||||
#if col == self.COL_DBMS:
|
||||
# if new_text not in Configuration.DATABASE_TYPES:
|
||||
# valid = False
|
||||
|
||||
if valid:
|
||||
self.liststore[path][col] = new_text
|
||||
|
||||
self.config.set_db_parameters( db_server = self.liststore[path][self.COL_DBMS]
|
||||
, db_name = name
|
||||
, db_ip = self.liststore[path][self.COL_HOST]
|
||||
, db_user = self.liststore[path][self.COL_USER]
|
||||
, db_pass = self.liststore[path][self.COL_PASS] )
|
||||
|
||||
return
|
||||
|
||||
def check_new_name(self, path, new_text):
|
||||
name_ok = True
|
||||
for i,db in enumerate(self.liststore):
|
||||
if i != path and new_text == db[self.COL_NAME]:
|
||||
name_ok = False
|
||||
#TODO: popup an error message telling user names must be unique
|
||||
return name_ok
|
||||
|
||||
def addTextObjColumn(self, title, n):
|
||||
col = gtk.TreeViewColumn(title)
|
||||
self.listview.append_column(col)
|
||||
|
||||
cRenderT = gtk.CellRendererText()
|
||||
cRenderT.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
|
||||
col.pack_start(cRenderT, False)
|
||||
col.add_attribute(cRenderT, 'text', n)
|
||||
|
||||
cRenderP = gtk.CellRendererPixbuf()
|
||||
col.pack_start(cRenderP, False)
|
||||
col.add_attribute(cRenderP, 'stock-id', n+1)
|
||||
|
||||
col.set_max_width(1000)
|
||||
col.set_spacing(0) # no effect
|
||||
self.listcols.append(col)
|
||||
#col.set_clickable(True)
|
||||
#col.connect("clicked", self.sortCols, p)
|
||||
return(col)
|
||||
|
||||
def loadDbs(self):
|
||||
|
||||
self.liststore.clear()
|
||||
self.listcols = []
|
||||
self.dbs = [] # list of tuples: (dbms, name, comment, user, passwd, host, status, icon)
|
||||
|
||||
try:
|
||||
# want to fill: dbms, name, comment, user, passwd, host, status(, icon?)
|
||||
for name in self.config.supported_databases: #db_ip/db_user/db_pass/db_server
|
||||
dbms = self.config.supported_databases[name].db_server # mysql/postgresql/sqlite
|
||||
dbms_num = self.config.get_backend(dbms) # 2 / 3 / 4
|
||||
comment = ""
|
||||
if dbms == 'sqlite':
|
||||
user = ""
|
||||
passwd = ""
|
||||
else:
|
||||
user = self.config.supported_databases[name].db_user
|
||||
passwd = self.config.supported_databases[name].db_pass
|
||||
host = self.config.supported_databases[name].db_ip
|
||||
status = ""
|
||||
icon = None
|
||||
err_msg = ""
|
||||
|
||||
db = Database.Database(self.config, sql = None, autoconnect = False)
|
||||
# try to connect to db, set status and err_msg if it fails
|
||||
try:
|
||||
# is creating empty db for sqlite ... mod db.py further?
|
||||
# add noDbTables flag to db.py?
|
||||
db.connect(backend=dbms_num, host=host, database=name, user=user, password=passwd, create=False)
|
||||
if db.connected:
|
||||
status = 'ok'
|
||||
icon = gtk.STOCK_APPLY
|
||||
if db.wrongDbVersion:
|
||||
status = 'old'
|
||||
icon = gtk.STOCK_INFO
|
||||
except Exceptions.FpdbMySQLAccessDenied:
|
||||
err_msg = "MySQL Server reports: Access denied. Are your permissions set correctly?"
|
||||
status = "failed"
|
||||
icon = gtk.STOCK_CANCEL
|
||||
except Exceptions.FpdbMySQLNoDatabase:
|
||||
err_msg = "MySQL client reports: 2002 or 2003 error. Unable to connect - " \
|
||||
+ "Please check that the MySQL service has been started"
|
||||
status = "failed"
|
||||
icon = gtk.STOCK_CANCEL
|
||||
except Exceptions.FpdbPostgresqlAccessDenied:
|
||||
err_msg = "Postgres Server reports: Access denied. Are your permissions set correctly?"
|
||||
status = "failed"
|
||||
except Exceptions.FpdbPostgresqlNoDatabase:
|
||||
err_msg = "Postgres client reports: Unable to connect - " \
|
||||
+ "Please check that the Postgres service has been started"
|
||||
status = "failed"
|
||||
icon = gtk.STOCK_CANCEL
|
||||
except:
|
||||
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||
log.info( 'db connection to '+str(dbms_num)+','+host+','+name+','+user+','+passwd+' failed: '
|
||||
+ err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]) )
|
||||
status = "failed"
|
||||
icon = gtk.STOCK_CANCEL
|
||||
|
||||
b = gtk.Button(name)
|
||||
b.show()
|
||||
iter = self.liststore.append( (dbms, name, comment, user, passwd, host, status, icon) )
|
||||
|
||||
self.listview.show()
|
||||
scrolledwindow.show()
|
||||
self.vbox.show()
|
||||
self.dia.set_focus(self.listview)
|
||||
|
||||
self.vbox.show_all()
|
||||
self.dia.show()
|
||||
except:
|
||||
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||
print 'loaddbs error: '+str(dbms_num)+','+host+','+name+','+user+','+passwd+' failed: ' \
|
||||
+ err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
|
||||
|
||||
def sortCols(self, col, n):
|
||||
try:
|
||||
if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING:
|
||||
col.set_sort_order(gtk.SORT_DESCENDING)
|
||||
else:
|
||||
col.set_sort_order(gtk.SORT_ASCENDING)
|
||||
self.liststore.set_sort_column_id(n, col.get_sort_order())
|
||||
#self.liststore.set_sort_func(n, self.sortnums, (n,grid))
|
||||
for i in xrange(len(self.listcols)):
|
||||
self.listcols[i].set_sort_indicator(False)
|
||||
self.listcols[n].set_sort_indicator(True)
|
||||
# use this listcols[col].set_sort_indicator(True)
|
||||
# to turn indicator off for other cols
|
||||
except:
|
||||
err = traceback.extract_tb(sys.exc_info()[2])
|
||||
print "***sortCols error: " + str(sys.exc_info()[1])
|
||||
print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] )
|
||||
|
||||
def refresh(self, widget, data):
|
||||
self.loadDbs()
|
||||
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
|
||||
config = Configuration.Config()
|
||||
|
||||
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
win.set_title("Test Log Viewer")
|
||||
win.set_border_width(1)
|
||||
win.set_default_size(600, 500)
|
||||
win.set_resizable(True)
|
||||
|
||||
dia = gtk.Dialog("Log Viewer",
|
||||
win,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
|
||||
dia.set_default_size(500, 500)
|
||||
log = GuiLogView(config, win, dia.vbox)
|
||||
response = dia.run()
|
||||
if response == gtk.RESPONSE_ACCEPT:
|
||||
pass
|
||||
dia.destroy()
|
||||
|
||||
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
, ["hand", False, "Hand", 0.0, "%s", "str"] # true not allowed for this line
|
||||
, ["plposition", False, "Posn", 1.0, "%s", "str"] # true not allowed for this line (set in code)
|
||||
, ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code)
|
||||
, ["n", True, "Hds", 1.0, "%d", "str"]
|
||||
, ["n", True, "Hds", 1.0, "%1.0f", "str"]
|
||||
, ["avgseats", False, "Seats", 1.0, "%3.1f", "str"]
|
||||
, ["vpip", True, "VPIP", 1.0, "%3.1f", "str"]
|
||||
, ["pfr", True, "PFR", 1.0, "%3.1f", "str"]
|
||||
|
@ -482,6 +482,22 @@ class GuiPlayerStats (threading.Thread):
|
|||
gametest = "and gt.category IS NULL"
|
||||
query = query.replace("<game_test>", gametest)
|
||||
|
||||
sitetest = ""
|
||||
q = []
|
||||
for m in self.filters.display.items():
|
||||
if m[0] == 'Sites' and m[1]:
|
||||
for n in sitenos:
|
||||
q.append(n)
|
||||
if len(q) > 0:
|
||||
sitetest = str(tuple(q))
|
||||
sitetest = sitetest.replace("L", "")
|
||||
sitetest = sitetest.replace(",)",")")
|
||||
sitetest = sitetest.replace("u'","'")
|
||||
sitetest = "and gt.siteId in %s" % sitetest
|
||||
else:
|
||||
sitetest = "and gt.siteId IS NULL"
|
||||
query = query.replace("<site_test>", sitetest)
|
||||
|
||||
if seats:
|
||||
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
|
||||
if 'show' in seats and seats['show']:
|
||||
|
@ -520,7 +536,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
blindtest = str(tuple(nolims))
|
||||
blindtest = blindtest.replace("L", "")
|
||||
blindtest = blindtest.replace(",)",")")
|
||||
bbtest = bbtest + blindtest + ' ) ) )'
|
||||
bbtest = bbtest + blindtest + ' ) )'
|
||||
else:
|
||||
bbtest = bbtest + '(-1) ) )'
|
||||
if type == 'ring':
|
||||
|
@ -539,7 +555,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
query = query.replace("<orderbyhgameTypeId>", "")
|
||||
groupLevels = "show" not in str(limits)
|
||||
if groupLevels:
|
||||
query = query.replace("<hgameTypeId>", "p.name")
|
||||
query = query.replace("<hgameTypeId>", "-1")
|
||||
else:
|
||||
query = query.replace("<hgameTypeId>", "h.gameTypeId")
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ import Hud
|
|||
|
||||
|
||||
# get config and set up logger
|
||||
c = Configuration.Config(file=options.config)
|
||||
c = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||
log = Configuration.get_logger("logging.conf", "hud", log_dir=c.dir_log, log_file='HUD-log.txt')
|
||||
|
||||
|
||||
|
@ -237,7 +237,7 @@ class HUD_main(object):
|
|||
try:
|
||||
(table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \
|
||||
self.db_connection.get_table_info(new_hand_id)
|
||||
except Exception, err:
|
||||
except Exception:
|
||||
log.error("db error: skipping %s" % new_hand_id)
|
||||
continue
|
||||
t1 = time.time()
|
||||
|
|
|
@ -125,6 +125,7 @@ class Hand(object):
|
|||
# currency symbol for this hand
|
||||
self.sym = self.SYMBOL[self.gametype['currency']] # save typing! delete this attr when done
|
||||
self.pot.setSym(self.sym)
|
||||
self.is_duplicate = False # i.e. don't update hudcache if true
|
||||
|
||||
def __str__(self):
|
||||
vars = ( ("BB", self.bb),
|
||||
|
@ -236,6 +237,7 @@ db: a connected Database object"""
|
|||
# TourneysPlayers
|
||||
else:
|
||||
log.info("Hand.insert(): hid #: %s is a duplicate" % hh['siteHandNo'])
|
||||
self.is_duplicate = True # i.e. don't update hudcache
|
||||
raise FpdbHandDuplicate(hh['siteHandNo'])
|
||||
|
||||
def updateHudCache(self, db):
|
||||
|
@ -321,7 +323,9 @@ For sites (currently only Carbon Poker) which record "all in" as a special actio
|
|||
self.stacks[player] -= Decimal(ante)
|
||||
act = (player, 'posts', "ante", ante, self.stacks[player]==0)
|
||||
self.actions['BLINDSANTES'].append(act)
|
||||
self.pot.addMoney(player, Decimal(ante))
|
||||
# self.pot.addMoney(player, Decimal(ante))
|
||||
self.pot.addCommonMoney(player, Decimal(ante))
|
||||
#I think the antes should be common money, don't have enough hand history to check
|
||||
|
||||
def addBlind(self, player, blindtype, amount):
|
||||
# if player is None, it's a missing small blind.
|
||||
|
@ -340,14 +344,16 @@ For sites (currently only Carbon Poker) which record "all in" as a special actio
|
|||
self.actions['BLINDSANTES'].append(act)
|
||||
|
||||
if blindtype == 'both':
|
||||
# work with the real ammount. limit games are listed as $1, $2, where
|
||||
# the SB 0.50 and the BB is $1, after the turn the minimum bet amount is $2....
|
||||
amount = self.bb
|
||||
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
||||
self.pot.addCommonMoney(Decimal(self.sb))
|
||||
self.pot.addCommonMoney(player, Decimal(self.sb))
|
||||
|
||||
if blindtype == 'secondsb':
|
||||
amount = Decimal(0)
|
||||
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
||||
self.pot.addCommonMoney(Decimal(self.sb))
|
||||
self.pot.addCommonMoney(player, Decimal(self.sb))
|
||||
|
||||
self.bets['PREFLOP'][player].append(Decimal(amount))
|
||||
self.pot.addMoney(player, Decimal(amount))
|
||||
|
@ -511,9 +517,6 @@ Card ranks will be uppercased
|
|||
for entry in self.collected:
|
||||
self.totalcollected += Decimal(entry[1])
|
||||
|
||||
|
||||
|
||||
|
||||
def getGameTypeAsString(self):
|
||||
"""\
|
||||
Map the tuple self.gametype onto the pokerstars string describing it
|
||||
|
@ -674,6 +677,7 @@ class HoldemOmahaHand(Hand):
|
|||
if self.maxseats is None:
|
||||
self.maxseats = hhc.guessMaxSeats(self)
|
||||
hhc.readOther(self)
|
||||
#print "\nHand:\n"+str(self)
|
||||
elif builtFrom == "DB":
|
||||
if handid is not None:
|
||||
self.select(handid) # Will need a handId
|
||||
|
@ -991,11 +995,12 @@ class DrawHand(Hand):
|
|||
self.lastBet['DEAL'] = Decimal(amount)
|
||||
elif blindtype == 'both':
|
||||
# extra small blind is 'dead'
|
||||
self.lastBet['DEAL'] = Decimal(self.bb)
|
||||
amount = Decimal(amount)/3
|
||||
amount += amount
|
||||
self.lastBet['DEAL'] = Decimal(amount)
|
||||
self.posted = self.posted + [[player,blindtype]]
|
||||
#print "DEBUG: self.posted: %s" %(self.posted)
|
||||
|
||||
|
||||
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
|
||||
if player == self.hero: # we have hero's cards just update shown/mucked
|
||||
if shown: self.shown.add(player)
|
||||
|
@ -1410,7 +1415,7 @@ class Pot(object):
|
|||
self.contenders = set()
|
||||
self.committed = {}
|
||||
self.streettotals = {}
|
||||
self.common = Decimal(0)
|
||||
self.common = {}
|
||||
self.total = None
|
||||
self.returned = {}
|
||||
self.sym = u'$' # this is the default currency symbol
|
||||
|
@ -1420,13 +1425,14 @@ class Pot(object):
|
|||
|
||||
def addPlayer(self,player):
|
||||
self.committed[player] = Decimal(0)
|
||||
self.common[player] = Decimal(0)
|
||||
|
||||
def addFold(self, player):
|
||||
# addFold must be called when a player folds
|
||||
self.contenders.discard(player)
|
||||
|
||||
def addCommonMoney(self, amount):
|
||||
self.common += amount
|
||||
def addCommonMoney(self, player, amount):
|
||||
self.common[player] += amount
|
||||
|
||||
def addMoney(self, player, amount):
|
||||
# addMoney must be called for any actions that put money in the pot, in the order they occur
|
||||
|
@ -1434,7 +1440,7 @@ class Pot(object):
|
|||
self.committed[player] += amount
|
||||
|
||||
def markTotal(self, street):
|
||||
self.streettotals[street] = sum(self.committed.values()) + self.common
|
||||
self.streettotals[street] = sum(self.committed.values()) + sum(self.common.values())
|
||||
|
||||
def getTotalAtStreet(self, street):
|
||||
if street in self.streettotals:
|
||||
|
@ -1442,11 +1448,11 @@ class Pot(object):
|
|||
return 0
|
||||
|
||||
def end(self):
|
||||
self.total = sum(self.committed.values()) + self.common
|
||||
self.total = sum(self.committed.values()) + sum(self.common.values())
|
||||
|
||||
# Return any uncalled bet.
|
||||
committed = sorted([ (v,k) for (k,v) in self.committed.items()])
|
||||
print "DEBUG: committed: %s" % committed
|
||||
#print "DEBUG: committed: %s" % committed
|
||||
#ERROR below. lastbet is correct in most cases, but wrong when
|
||||
# additional money is committed to the pot in cash games
|
||||
# due to an additional sb being posted. (Speculate that
|
||||
|
|
|
@ -69,6 +69,7 @@ out_path (default '-' = sys.stdout)
|
|||
follow : whether to tail -f the input"""
|
||||
|
||||
self.config = config
|
||||
self.import_parameters = self.config.get_import_parameters()
|
||||
#log = Configuration.get_logger("logging.conf", "parser", log_dir=self.config.dir_log)
|
||||
log.info("HandHistory init - %s subclass, in_path '%s'; out_path '%s'" % (self.sitename, in_path, out_path) )
|
||||
|
||||
|
@ -87,12 +88,8 @@ follow : whether to tail -f the input"""
|
|||
|
||||
if in_path == '-':
|
||||
self.in_fh = sys.stdin
|
||||
self.out_fh = get_out_fh(out_path, self.import_parameters)
|
||||
|
||||
if out_path == '-':
|
||||
self.out_fh = sys.stdout
|
||||
else:
|
||||
# TODO: out_path should be sanity checked.
|
||||
self.out_fh = sys.stdout
|
||||
self.follow = follow
|
||||
self.compiledPlayers = set()
|
||||
self.maxseats = 10
|
||||
|
@ -446,8 +443,8 @@ or None if we fail to get the info """
|
|||
def guessMaxSeats(self, hand):
|
||||
"""Return a guess at maxseats when not specified in HH."""
|
||||
# if some other code prior to this has already set it, return it
|
||||
if maxseats > 1 and maxseats < 11:
|
||||
return maxseats
|
||||
if self.maxseats > 1 and self.maxseats < 11:
|
||||
return self.maxseats
|
||||
mo = self.maxOccSeat(hand)
|
||||
|
||||
if mo == 10: return 10 #that was easy
|
||||
|
@ -515,3 +512,23 @@ def getSiteHhc(config, sitename):
|
|||
hhcName = config.supported_sites[sitename].converter
|
||||
hhcModule = __import__(hhcName)
|
||||
return getattr(hhcModule, hhcName[:-6])
|
||||
|
||||
def get_out_fh(out_path, parameters):
|
||||
if out_path == '-':
|
||||
return(sys.stdout)
|
||||
elif parameters['saveStarsHH']:
|
||||
out_dir = os.path.dirname(out_path)
|
||||
if not os.path.isdir(out_dir) and out_dir != '':
|
||||
try:
|
||||
os.makedirs(out_dir)
|
||||
except: # we get a WindowsError here in Windows.. pretty sure something else for Linux :D
|
||||
log.error("Unable to create output directory %s for HHC!" % out_dir)
|
||||
print "*** ERROR: UNABLE TO CREATE OUTPUT DIRECTORY", out_dir
|
||||
else:
|
||||
log.info("Created directory '%s'" % out_dir)
|
||||
try:
|
||||
return(codecs.open(out_path, 'w', 'utf8'))
|
||||
except:
|
||||
log.error("out_path %s couldn't be opened" % (out_path))
|
||||
else:
|
||||
return(sys.stdout)
|
||||
|
|
|
@ -27,7 +27,7 @@ def fpdb_options():
|
|||
action="store_true",
|
||||
help="If passed error output will go to the console rather than .")
|
||||
parser.add_option("-d", "--databaseName",
|
||||
dest="dbname", default="fpdb",
|
||||
dest="dbname",
|
||||
help="Overrides the default database name")
|
||||
parser.add_option("-c", "--configFile",
|
||||
dest="config", default=None,
|
||||
|
|
|
@ -77,14 +77,15 @@ class PartyPoker(HandHistoryConverter):
|
|||
re.VERBOSE)
|
||||
|
||||
re_HandInfo = re.compile("""
|
||||
^Table\s+(?P<TTYPE>[$a-zA-Z0-9 ]+)\s+
|
||||
^Table\s+(?P<TTYPE>[$a-zA-Z0-9 ]+)?\s+
|
||||
(?: \#|\(|)(?P<TABLE>\d+)\)?\s+
|
||||
(?:[a-zA-Z0-9 ]+\s+\#(?P<MTTTABLE>\d+).+)?
|
||||
(\(No\sDP\)\s)?
|
||||
\((?P<PLAY>Real|Play)\s+Money\)\s+ # FIXME: check if play money is correct
|
||||
Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
|
||||
\s+Total\s+number\s+of\s+players\s+\:\s+(?P<PLYRS>\d+)/?(?P<MAX>\d+)?
|
||||
""",
|
||||
re.VERBOSE|re.MULTILINE)
|
||||
re.VERBOSE|re.MULTILINE|re.DOTALL)
|
||||
|
||||
re_CountedSeats = re.compile("^Total\s+number\s+of\s+players\s*:\s*(?P<COUNTED_SEATS>\d+)", re.MULTILINE)
|
||||
re_SplitHands = re.compile('\x00+')
|
||||
|
@ -106,7 +107,6 @@ class PartyPoker(HandHistoryConverter):
|
|||
def guessMaxSeats(self, hand):
|
||||
"""Return a guess at max_seats when not specified in HH."""
|
||||
mo = self.maxOccSeat(hand)
|
||||
|
||||
if mo == 10: return mo
|
||||
if mo == 2: return 2
|
||||
if mo <= 6: return 6
|
||||
|
@ -260,6 +260,7 @@ class PartyPoker(HandHistoryConverter):
|
|||
for i,v in enumerate(self.collected):
|
||||
if v[0] in self.pot.returned:
|
||||
self.collected[i][1] = Decimal(v[1]) - self.pot.returned[v[0]]
|
||||
self.collectees[v[0]] -= self.pot.returned[v[0]]
|
||||
return origTotalPot()
|
||||
return totalPot
|
||||
instancemethod = type(hand.totalPot)
|
||||
|
@ -313,6 +314,9 @@ class PartyPoker(HandHistoryConverter):
|
|||
if key == 'PLAY' and info['PLAY'] != 'Real':
|
||||
# if realy party doesn's save play money hh
|
||||
hand.gametype['currency'] = 'play'
|
||||
if key == 'MAX' and info[key] is not None:
|
||||
hand.maxseats = int(info[key])
|
||||
|
||||
|
||||
def readButton(self, hand):
|
||||
m = self.re_Button.search(hand.handText)
|
||||
|
@ -465,8 +469,9 @@ class PartyPoker(HandHistoryConverter):
|
|||
def getTableTitleRe(type, table_name=None, tournament = None, table_number=None):
|
||||
"Returns string to search in windows titles"
|
||||
if type=="tour":
|
||||
print 'party', 'getTableTitleRe', "%s.+Table\s#%s" % (table_name, table_number)
|
||||
return "%s.+Table\s#%s" % (table_name, table_number)
|
||||
TableName = table_name.split(" ")
|
||||
print 'party', 'getTableTitleRe', "%s.+Table\s#%s" % (TableName[0], table_number)
|
||||
return "%s.+Table\s#%s" % (TableName[0], table_number)
|
||||
else:
|
||||
print 'party', 'getTableTitleRe', table_number
|
||||
return table_name
|
||||
|
|
22
pyfpdb/PokerStarsToFpdb.py
Executable file → Normal file
22
pyfpdb/PokerStarsToFpdb.py
Executable file → Normal file
|
@ -140,6 +140,14 @@ class PokerStars(HandHistoryConverter):
|
|||
|
||||
mg = m.groupdict()
|
||||
# translations from captured groups to fpdb info strings
|
||||
Lim_Blinds = { '0.04': ('0.01', '0.02'), '0.10': ('0.02', '0.05'), '0.20': ('0.05', '0.10'),
|
||||
'0.50': ('0.10', '0.25'), '1.00': ('0.25', '0.50'), '2.00': ('0.50', '1.00'),
|
||||
'4.00': ('1.00', '2.00'), '6.00': ('1.00', '3.00'), '10.00': ('2.00', '5.00'),
|
||||
'20.00': ('5.00', '10.00'), '30.00': ('10.00', '15.00'), '60.00': ('15.00', '30.00'),
|
||||
'100.00': ('25.00', '50.00'),'200.00': ('50.00', '100.00'),'400.00': ('100.00', '200.00'),
|
||||
'1000.00': ('250.00', '500.00')}
|
||||
|
||||
|
||||
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
||||
games = { # base, category
|
||||
"Hold'em" : ('hold','holdem'),
|
||||
|
@ -173,6 +181,10 @@ class PokerStars(HandHistoryConverter):
|
|||
else:
|
||||
info['type'] = 'tour'
|
||||
|
||||
if info['limitType'] == 'fl' and info['bb'] is not None and info['type'] == 'ring' and info['base'] != 'stud':
|
||||
info['sb'] = Lim_Blinds[mg['BB']][0]
|
||||
info['bb'] = Lim_Blinds[mg['BB']][1]
|
||||
|
||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||
return info
|
||||
|
||||
|
@ -287,16 +299,14 @@ class PokerStars(HandHistoryConverter):
|
|||
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
||||
|
||||
def readBlinds(self, hand):
|
||||
try:
|
||||
count = 0
|
||||
liveBlind = True
|
||||
for a in self.re_PostSB.finditer(hand.handText):
|
||||
if count == 0:
|
||||
if liveBlind:
|
||||
hand.addBlind(a.group('PNAME'), 'small blind', a.group('SB'))
|
||||
count = 1
|
||||
liveBlind = False
|
||||
else:
|
||||
# Post dead blinds as ante
|
||||
hand.addBlind(a.group('PNAME'), 'secondsb', a.group('SB'))
|
||||
except: # no small blind
|
||||
hand.addBlind(None, None, None)
|
||||
for a in self.re_PostBB.finditer(hand.handText):
|
||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||
for a in self.re_PostBoth.finditer(hand.handText):
|
||||
|
|
|
@ -1346,6 +1346,7 @@ class Sql:
|
|||
|
||||
# same as above except stats are aggregated for all blind/limit levels
|
||||
self.query['get_stats_from_hand_aggregated'] = """
|
||||
/* explain query plan */
|
||||
SELECT hc.playerId AS player_id,
|
||||
max(case when hc.gametypeId = h.gametypeId
|
||||
then hp.seatNo
|
||||
|
@ -1915,6 +1916,7 @@ class Sql:
|
|||
inner join Players p on (p.Id = hp.playerId)
|
||||
where hp.playerId in <player_test>
|
||||
<game_test>
|
||||
<site_test>
|
||||
/*and hp.tourneysPlayersId IS NULL*/
|
||||
and h.seats <seats_test>
|
||||
<flagtest>
|
||||
|
@ -1999,6 +2001,7 @@ class Sql:
|
|||
inner join Players p on (p.Id = hp.playerId)
|
||||
where hp.playerId in <player_test>
|
||||
<game_test>
|
||||
<site_test>
|
||||
/*and hp.tourneysPlayersId IS NULL*/
|
||||
and h.seats <seats_test>
|
||||
<flagtest>
|
||||
|
@ -2076,8 +2079,7 @@ class Sql:
|
|||
,100.0*avg((hp.totalProfit+hp.rake)/(gt.bigBlind+0.0)) AS bb100xr
|
||||
,avg((hp.totalProfit+hp.rake)/100.0) AS profhndxr
|
||||
,avg(h.seats+0.0) AS avgseats
|
||||
/*,variance(hp.totalProfit/100.0) AS variance*/
|
||||
,0.0 AS variance
|
||||
,variance(hp.totalProfit/100.0) AS variance
|
||||
from HandsPlayers hp
|
||||
inner join Hands h on (h.id = hp.handId)
|
||||
inner join Gametypes gt on (gt.Id = h.gameTypeId)
|
||||
|
@ -2085,6 +2087,7 @@ class Sql:
|
|||
inner join Players p on (p.Id = hp.playerId)
|
||||
where hp.playerId in <player_test>
|
||||
<game_test>
|
||||
<site_test>
|
||||
/*and hp.tourneysPlayersId IS NULL*/
|
||||
and h.seats <seats_test>
|
||||
<flagtest>
|
||||
|
|
|
@ -32,12 +32,16 @@ import gtk
|
|||
import gobject
|
||||
|
||||
# fpdb/free poker tools modules
|
||||
import Configuration
|
||||
from HandHistoryConverter import getTableTitleRe
|
||||
|
||||
# get the correct module for the current os
|
||||
if os.name == 'posix':
|
||||
import XTables as Tables
|
||||
elif os.name == 'nt':
|
||||
import WinTables as Tables
|
||||
|
||||
config = Configuration.Config()
|
||||
# Main function used for testing
|
||||
if __name__=="__main__":
|
||||
# c = Configuration.Config()
|
||||
|
@ -82,11 +86,16 @@ if __name__=="__main__":
|
|||
(tour_no, tab_no) = table_name.split(",", 1)
|
||||
tour_no = tour_no.rstrip()
|
||||
tab_no = tab_no.rstrip()
|
||||
table = Tables.Table(None, tournament = tour_no, table_number = tab_no)
|
||||
type = "tour"
|
||||
table_kwargs = dict(tournament = tour_no, table_number = tab_no)
|
||||
else: # not a tournament
|
||||
print "cash game"
|
||||
table_name = table_name.rstrip()
|
||||
table = Tables.Table(None, table_name = table_name)
|
||||
type = "cash"
|
||||
table_kwargs = dict(table_name = table_name)
|
||||
|
||||
search_string = getTableTitleRe(config, "Full Tilt Poker", type, **table_kwargs)
|
||||
table = Tables.Table(search_string, **table_kwargs)
|
||||
table.gdk_handle = gtk.gdk.window_foreign_new(table.number)
|
||||
|
||||
print "table =", table
|
||||
|
|
|
@ -68,7 +68,7 @@ class Table(Table_Window):
|
|||
window_number = None
|
||||
for listing in os.popen('xwininfo -root -tree').readlines():
|
||||
if re.search(search_string, listing):
|
||||
print listing
|
||||
# print listing
|
||||
mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing)
|
||||
self.number = int( mo.group(1), 0)
|
||||
self.width = int( mo.group(4) )
|
||||
|
@ -89,7 +89,6 @@ class Table(Table_Window):
|
|||
# break
|
||||
|
||||
if window_number is None:
|
||||
print "Window %s not found. Skipping." % search_string
|
||||
return None
|
||||
|
||||
# my_geo = self.window.get_geometry()
|
||||
|
|
|
@ -97,6 +97,7 @@ except:
|
|||
|
||||
import GuiPrefs
|
||||
import GuiLogView
|
||||
import GuiDatabase
|
||||
import GuiBulkImport
|
||||
import GuiPlayerStats
|
||||
import GuiPositionalStats
|
||||
|
@ -288,11 +289,32 @@ class fpdb:
|
|||
|
||||
dia.destroy()
|
||||
|
||||
def dia_create_del_database(self, widget, data=None):
|
||||
self.warning_box("Unimplemented: Create/Delete Database")
|
||||
self.obtain_global_lock()
|
||||
def dia_maintain_dbs(self, widget, data=None):
|
||||
self.warning_box("Unimplemented: Maintain Databases")
|
||||
return
|
||||
if len(self.tab_names) == 1:
|
||||
if self.obtain_global_lock(): # returns true if successful
|
||||
# only main tab has been opened, open dialog
|
||||
dia = gtk.Dialog("Maintain Databases",
|
||||
self.window,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
|
||||
gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT))
|
||||
dia.set_default_size(700, 320)
|
||||
|
||||
prefs = GuiDatabase.GuiDatabase(self.config, self.window, dia)
|
||||
response = dia.run()
|
||||
if response == gtk.RESPONSE_ACCEPT:
|
||||
# save updated config
|
||||
self.config.save()
|
||||
|
||||
self.release_global_lock()
|
||||
|
||||
dia.destroy()
|
||||
else:
|
||||
self.warning_box("Cannot open Database Maintenance window because "
|
||||
+ "other windows have been opened. Re-start fpdb to use this option.")
|
||||
|
||||
def dia_create_del_user(self, widget, data=None):
|
||||
self.warning_box("Unimplemented: Create/Delete user")
|
||||
self.obtain_global_lock()
|
||||
|
@ -620,7 +642,7 @@ class fpdb:
|
|||
<menuitem action="tableviewer"/>
|
||||
</menu>
|
||||
<menu action="database">
|
||||
<menuitem action="createdb"/>
|
||||
<menuitem action="maintaindbs"/>
|
||||
<menuitem action="createuser"/>
|
||||
<menuitem action="createtabs"/>
|
||||
<menuitem action="rebuildhudcache"/>
|
||||
|
@ -663,7 +685,7 @@ class fpdb:
|
|||
('sessionreplay', None, '_Session Replayer (todo)', None, 'Session Replayer (todo)', self.not_implemented),
|
||||
('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer),
|
||||
('database', None, '_Database'),
|
||||
('createdb', None, 'Create or Delete _Database (todo)', None, 'Create or Delete Database', self.dia_create_del_database),
|
||||
('maintaindbs', None, '_Maintain Databases (todo)', None, 'Maintain Databases', self.dia_maintain_dbs),
|
||||
('createuser', None, 'Create or Delete _User (todo)', None, 'Create or Delete User', self.dia_create_del_user),
|
||||
('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables),
|
||||
('rebuildhudcache', None, 'Rebuild HUD Cache', None, 'Rebuild HUD Cache', self.dia_recreate_hudcache),
|
||||
|
@ -685,9 +707,15 @@ class fpdb:
|
|||
window.add_accel_group(accel_group)
|
||||
return menubar
|
||||
|
||||
def load_profile(self):
|
||||
def load_profile(self, create_db = False):
|
||||
"""Loads profile from the provided path name."""
|
||||
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||
if self.config.file_error:
|
||||
self.warning_box( "There is an error in your config file\n" + self.config.file
|
||||
+ "\n\nError is: " + str(self.config.file_error)
|
||||
, diatitle="CONFIG FILE ERROR" )
|
||||
exit()
|
||||
|
||||
log = Configuration.get_logger("logging.conf", "fpdb", log_dir=self.config.dir_log)
|
||||
print "Logfile is " + os.path.join(self.config.dir_log, self.config.log_file) + "\n"
|
||||
if self.config.example_copy:
|
||||
|
@ -905,7 +933,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
|||
self.tab_main_help(None, None)
|
||||
|
||||
self.window.show()
|
||||
self.load_profile()
|
||||
self.load_profile(create_db = True)
|
||||
|
||||
if not options.errorsToConsole:
|
||||
fileName = os.path.join(self.config.dir_log, 'fpdb-errors.txt')
|
||||
|
@ -997,6 +1025,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
|||
return response
|
||||
|
||||
def validate_config(self):
|
||||
if self.config.get_import_parameters().get('saveStarsHH'):
|
||||
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
||||
hhbase = os.path.expanduser(hhbase)
|
||||
#hhdir = os.path.join(hhbase,site)
|
||||
|
|
|
@ -456,7 +456,7 @@ class Importer:
|
|||
# FIXME: Need to test for bulk import that isn't rebuilding the cache
|
||||
if self.callHud:
|
||||
for hand in handlist:
|
||||
if hand is not None:
|
||||
if hand is not None and not hand.is_duplicate:
|
||||
hand.updateHudCache(self.database)
|
||||
self.database.commit()
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ from distutils.core import setup
|
|||
import py2exe
|
||||
import glob
|
||||
import matplotlib
|
||||
import shutil
|
||||
from datetime import date
|
||||
|
||||
|
||||
|
@ -111,7 +112,7 @@ def test_and_remove(top):
|
|||
# remove build and dist dirs if they exist
|
||||
test_and_remove('dist')
|
||||
test_and_remove('build')
|
||||
test_and_remove('gfx')
|
||||
#test_and_remove('gfx')
|
||||
|
||||
|
||||
today = date.today().strftime('%Y%m%d')
|
||||
|
@ -151,7 +152,7 @@ setup(
|
|||
},
|
||||
|
||||
# files in 2nd value in tuple are moved to dir named in 1st value
|
||||
data_files = [('', ['HUD_config.xml.example', 'Cards01.png', 'logging.conf'])
|
||||
data_files = [('', ['HUD_config.xml.example', 'Cards01.png', 'logging.conf', '../docs/readme.txt'])
|
||||
,(dist_dir, [r'..\run_fpdb.bat'])
|
||||
,( dist_dir + r'\gfx', glob.glob(r'..\gfx\*.*') )
|
||||
# line below has problem with fonts subdir ('not a regular file')
|
||||
|
@ -174,3 +175,36 @@ dest = dest.replace('\\', '\\\\')
|
|||
os.rename( 'pyfpdb', dest )
|
||||
|
||||
|
||||
print "Enter directory name for GTK 2.14 (e.g. c:\code\gtk_2.14.7-20090119)\n: ", # the comma means no newline
|
||||
gtk_dir = sys.stdin.readline().rstrip()
|
||||
|
||||
|
||||
print "\ncopying files and dirs from ", gtk_dir, "to", dest.replace('\\\\', '\\'), "..."
|
||||
src = os.path.join(gtk_dir, 'bin', 'libgdk-win32-2.0-0.dll')
|
||||
src = src.replace('\\', '\\\\')
|
||||
shutil.copy( src, dest )
|
||||
|
||||
src = os.path.join(gtk_dir, 'bin', 'libgobject-2.0-0.dll')
|
||||
src = src.replace('\\', '\\\\')
|
||||
shutil.copy( src, dest )
|
||||
|
||||
|
||||
src_dir = os.path.join(gtk_dir, 'etc')
|
||||
src_dir = src_dir.replace('\\', '\\\\')
|
||||
dest_dir = os.path.join(dest, 'etc')
|
||||
dest_dir = dest_dir.replace('\\', '\\\\')
|
||||
shutil.copytree( src_dir, dest_dir )
|
||||
|
||||
src_dir = os.path.join(gtk_dir, 'lib')
|
||||
src_dir = src_dir.replace('\\', '\\\\')
|
||||
dest_dir = os.path.join(dest, 'lib')
|
||||
dest_dir = dest_dir.replace('\\', '\\\\')
|
||||
shutil.copytree( src_dir, dest_dir )
|
||||
|
||||
src_dir = os.path.join(gtk_dir, 'share')
|
||||
src_dir = src_dir.replace('\\', '\\\\')
|
||||
dest_dir = os.path.join(dest, 'share')
|
||||
dest_dir = dest_dir.replace('\\', '\\\\')
|
||||
shutil.copytree( src_dir, dest_dir )
|
||||
|
||||
|
||||
|
|
0
run_fpdb.py
Executable file → Normal file
0
run_fpdb.py
Executable file → Normal file
Loading…
Reference in New Issue
Block a user