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

This commit is contained in:
Gerko de Roo 2010-08-28 18:14:41 +02:00
commit 62892c80dc
52 changed files with 6540 additions and 2452 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
pyfpdb/HUD_config.xml.example -crlf

View File

@ -0,0 +1,32 @@
Hello everyone,
The new snapshot 0.20.905 is now available for download as source or as packages/installers for Debian, Gentoo, Ubuntu and Windows.
This version brings many improvements and bugfixes, updating is recommended for users of previous snapshots. If you're using a stable version like 0.20 or 0.20.1 please consider trying this version and report any bugs, and in particular regressions, so we can fix them. This snapshot will hopefully be the last, next step is one or more release candidates, and then the next stable release.
We are still looking for translators! You can find some information about what languages we are still missing here: http://sourceforge.net/apps/mediawiki/fpdb/index.php?title=Translation
188 changesets (excl. merges) have gone in since 0.20.904.
Please note that you will have to either recreate your database or use a new one if you're updating from 0.20.904 or older.
Config files from 0.20 and later should work. Please report if you have problems with config files from that version or later.
What's changed:
- Fpdb now supports running in languages other than English, Erki supplied a translation for Hungarian. French, Spanish and Italian are in progress by new contributors. Fpdb will use the system-configured language, a configuration option will be added before the next stable release. Note that this is about the user interface language, non-English history file parsing is a seperate topic.
- Much improved testing to improve the recording of data, especially corner cases and non-trivial stats
- OnGame network (which now includes Betfair) is now properly supported
- FTP.fr importing now works. We don't know if the HUD works yet, give it a go and let us know
- We noticed that fpdb already supports PS.fr
- PokerStars should support all limits now
- The Debian package now handles a missing config file properly
- Many minor improvements to the Gentoo ebuilds for the upcoming submission to the sunrise overlay
- We changed how email import is configured. Either delete your old config or see the HUD_config.xml.example file for how to add the section to the right place
- fpdb should now be able to run with any config file from 0.20 or later, including all 0.20.9* snapshots
- Some more fixes to window visibility and the minimise to tray icon feature
- Various other small cleanups, fixes and improvements. See the git changelog for full details
To download:
- Debian/Ubuntu Linux: http://sourceforge.net/projects/fpdb/files/fpdb/Snapshots/python-fpdb_0.20.905-1_all.deb/download
- Gentoo Linux: http://sourceforge.net/projects/fpdb/files/fpdb/Snapshots/fpdb-0.20.905.ebuild/download
- Windows: http://sourceforge.net/projects/fpdb/files/fpdb/Snapshots/fpdb-0.20.905anyCPU.exe/download
- Source version for those who installed the dependencies manually: http://sourceforge.net/projects/fpdb/files/fpdb/Snapshots/fpdb-0.20.905.tar.bz2/download
Thanks to everyone who contributed code, translations, testing and bug reports!
The fpdb team

View File

@ -1,3 +1,16 @@
free-poker-tools (0.20.906-1) unstable; urgency=low
* New snapshot
-- Mika Bostrom <bostik@iki.fi> Fri, 27 Aug 2010 08:26:05 +0300
free-poker-tools (0.20.905-1) unstable; urgency=low
* New snapshot
* Hungarian translation
-- Mika Bostrom <bostik@iki.fi> Wed, 25 Aug 2010 10:05:36 +0300
free-poker-tools (0.20.904-2) unstable; urgency=low free-poker-tools (0.20.904-2) unstable; urgency=low
* On fpdb start, copy example HUD_config.xml in place if none is present * On fpdb start, copy example HUD_config.xml in place if none is present

View File

@ -26,8 +26,7 @@ install: build
# Copy *.pyw manually in packaging tree # Copy *.pyw manually in packaging tree
cp pyfpdb/*.pyw debian/$(PACKAGE)/usr/lib/python2.6/site-packages/fpdb/ cp pyfpdb/*.pyw debian/$(PACKAGE)/usr/lib/python2.6/site-packages/fpdb/
# Remove scripts that are only useful in win32 # Remove scripts that are only useful in win32
rm debian/$(PACKAGE)/usr/lib/python2.6/site-packages/fpdb//windows_make_bats.py rm debian/$(PACKAGE)/usr/lib/python2.6/site-packages/fpdb/windows_make_bats.py
rm debian/$(PACKAGE)/usr/lib/python2.6/site-packages/fpdb/py2exe_setup.py
binary-indep: build install binary-indep: build install

View File

@ -17,7 +17,7 @@ SLOT="0"
KEYWORDS="~amd64 ~x86" KEYWORDS="~amd64 ~x86"
#note: this should work on other architectures too, please send me your experiences #note: this should work on other architectures too, please send me your experiences
IUSE="graph mysql postgres sqlite" IUSE="graph mysql postgres sqlite linguas_hu"
RDEPEND=" RDEPEND="
mysql? ( virtual/mysql mysql? ( virtual/mysql
dev-python/mysql-python ) dev-python/mysql-python )
@ -37,6 +37,11 @@ src_install() {
insinto "${GAMES_DATADIR}"/${PN} insinto "${GAMES_DATADIR}"/${PN}
doins -r gfx doins -r gfx
doins -r pyfpdb doins -r pyfpdb
if use linguas_hu; then
dosym "${GAMES_DATADIR}"/${PN}/pyfpdb/locale/hu/LC_MESSAGES/${PN}.mo /usr/share/locale/hu/LC_MESSAGES/${PN}.mo
fi
doins readme.txt doins readme.txt
exeinto "${GAMES_DATADIR}"/${PN} exeinto "${GAMES_DATADIR}"/${PN}

View File

@ -18,7 +18,7 @@ SLOT="0"
KEYWORDS="" KEYWORDS=""
#note: this should work on other architectures too, please send me your experiences #note: this should work on other architectures too, please send me your experiences
IUSE="graph mysql postgres sqlite" IUSE="graph mysql postgres sqlite linguas_hu"
RDEPEND=" RDEPEND="
mysql? ( virtual/mysql mysql? ( virtual/mysql
dev-python/mysql-python ) dev-python/mysql-python )
@ -42,6 +42,11 @@ src_install() {
insinto "${GAMES_DATADIR}"/${PN} insinto "${GAMES_DATADIR}"/${PN}
doins -r gfx doins -r gfx
doins -r pyfpdb doins -r pyfpdb
if use linguas_hu; then
dosym "${GAMES_DATADIR}"/${PN}/pyfpdb/locale/hu/LC_MESSAGES/${PN}.mo /usr/share/locale/hu/LC_MESSAGES/${PN}.mo
fi
doins readme.txt doins readme.txt
exeinto "${GAMES_DATADIR}"/${PN} exeinto "${GAMES_DATADIR}"/${PN}

View File

@ -103,6 +103,7 @@ Step 4 Get the fpdb GIT tree
---------------------------- ----------------------------
4.1/ Best to take a copy to work with; following steps will assume that the fpdb folder is on the Desktop 4.1/ Best to take a copy to work with; following steps will assume that the fpdb folder is on the Desktop
4.2/ Edit the script in packaging/windows/py2exe_setup.py to set the fpdbver variable for this release
5.3/ Install correct Numpy for this build 5.3/ Install correct Numpy for this build
@ -158,7 +159,7 @@ Step 6 Run py2exe to generate fpdb.exe
6.1/ Run the script to create the fpdb.exe bundle 6.1/ Run the script to create the fpdb.exe bundle
dos> cd Desktop\fpdb\pyfpdb dos> cd Desktop\fpdb\packaging\windows
dos> c:\python26\python.exe py2exe_setup.py py2exe dos> c:\python26\python.exe py2exe_setup.py py2exe
wait a while, watch lots of copying and whatever. wait a while, watch lots of copying and whatever.
@ -183,16 +184,14 @@ Step 8 Drag out the completed bundle
------------------------------------ ------------------------------------
py2exe creates a new folder for the created software bundle, drag this out to the desktop for ease of working. py2exe creates a new folder for the created software bundle, drag this out to the desktop for ease of working.
As far as I know you cannot rerun the build if the fpdb-yyyymmdd-exe exists in the tree, so dragging this out
also allows the build to re-run at step 6.
8.1/ Drag Desktop\fpdb\pyfpdb\fpdb-yyyymmdd-exe to Desktop\ 8.1/ Drag Desktop\fpdb\packaging\windows\fpdb-n.nn.nnn to Desktop\
Step 9 Initial run Step 9 Initial run
------------------ ------------------
9.1/ Open the Desktop\fpdb-yyyymmdd-exe folder 9.1/ Open the Desktop\fpdb-n.nn.nnn folder
9.2/ In explorer...tools...folder options...View uncheck "Hide extensions for known file types" 9.2/ In explorer...tools...folder options...View uncheck "Hide extensions for known file types"
9.3/ Double click run_fpdb.bat 9.3/ Double click run_fpdb.bat
9.4/ check the contents of pyfpdb\fpdb.exe.log, deal with any errors thrown 9.4/ check the contents of pyfpdb\fpdb.exe.log, deal with any errors thrown
@ -222,7 +221,7 @@ pyfpdb/share/man
Step 12 rename folder Step 12 rename folder
--------------------- ---------------------
Rename the folder to something meaningful to the community. If you have built for NoSSE, append anyCPU to the directory name. If needed, rename the folder to something meaningful to the community. If you have built for NoSSE, append anyCPU to the directory name.
Step 13 Compress to executable archive Step 13 Compress to executable archive

View File

@ -32,14 +32,10 @@ Py2exe script for fpdb.
#HOW TO USE this script: #HOW TO USE this script:
# #
#- cd to the folder where this script is stored, usually .../pyfpdb. #- cd to the folder where this script is stored, usually ...packaging/windows
# [If there are build and dist subfolders present , delete them to get #- Run the script with python "py2exe_setup.py py2exe"
# rid of earlier builds. Update: script now does this for you] #- You will frequently get messages about missing .dll files.just assume other
#- Run the script with "py2exe_setup.py py2exe" # person will have them? we have copyright issues including some dll's
#- You will frequently get messages about missing .dll files. E. g.,
# MSVCP90.dll. These are somewhere in your windows install, so you
# can just copy them to your working folder. (or just assume other
# person will have them? any copyright issues with including them?)
#- If it works, you'll have a new dir fpdb-YYYYMMDD-exe which should #- If it works, you'll have a new dir fpdb-YYYYMMDD-exe which should
# contain 2 dirs; gfx and pyfpdb and run_fpdb.bat # contain 2 dirs; gfx and pyfpdb and run_fpdb.bat
#- [ This bit is now automated: #- [ This bit is now automated:
@ -49,27 +45,11 @@ Py2exe script for fpdb.
#- You can (should) then prune the etc/, lib/ and share/ folders to #- You can (should) then prune the etc/, lib/ and share/ folders to
# remove components we don't need. (see output at end of program run) # remove components we don't need. (see output at end of program run)
# sqlcoder notes: this worked for me with the following notes:
#- I used the following versions:
# python 2.5.4
# gtk+ 2.14.7 (gtk_2.14.7-20090119)
# pycairo 1.4.12-2
# pygobject 2.14.2-2
# pygtk 2.12.1-3
# matplotlib 0.98.3
# numpy 1.4.0
# py2exe-0.6.9 for python 2.5
#
#- I also copied these dlls manually from <gtk>/bin to /dist :
#
# libgobject-2.0-0.dll
# libgdk-win32-2.0-0.dll
#
# Now updated to work with python 2.6 + related dependencies
# See walkthrough in packaging directory for versions used # See walkthrough in packaging directory for versions used
# Updates to this script have broken python 2.5 compatibility (gio module, msvcr71 references now msvcp90)
# steffeN: Doesnt seem necessary to gettext-ify this, but feel free to if you disagree # steffeN: Doesnt seem necessary to gettext-ify this, but feel free to if you disagree
# Gimick: restructure to allow script to run from packaging/windows directory, and not to write to source pyfpdb
import os import os
import sys import sys
@ -86,33 +66,14 @@ import py2exe
import glob import glob
import matplotlib import matplotlib
import shutil import shutil
from datetime import date #from datetime import date
origIsSystemDLL = py2exe.build_exe.isSystemDLL
def isSystemDLL(pathname): def isSystemDLL(pathname):
#dwmapi appears to be vista-specific file, not XP #dwmapi appears to be vista-specific file, not XP
if os.path.basename(pathname).lower() in ("dwmapi.dll"): if os.path.basename(pathname).lower() in ("dwmapi.dll"):
return 0 return 0
return origIsSystemDLL(pathname) return origIsSystemDLL(pathname)
py2exe.build_exe.isSystemDLL = isSystemDLL
def remove_tree(top):
# Delete everything reachable from the directory named in 'top',
# assuming there are no symbolic links.
# CAUTION: This is dangerous! For example, if top == '/', it
# could delete all your disk files.
# sc: Nicked this from somewhere, added the if statement to try
# make it a bit safer
if top in ('build','dist','pyfpdb',dist_dirname) and os.path.basename(os.getcwd()) == 'pyfpdb':
#print "removing directory '"+top+"' ..."
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
os.rmdir(top)
def test_and_remove(top): def test_and_remove(top):
if os.path.exists(top): if os.path.exists(top):
@ -122,30 +83,63 @@ def test_and_remove(top):
print "Unexpected file '"+top+"' found. Exiting." print "Unexpected file '"+top+"' found. Exiting."
exit() exit()
today = date.today().strftime('%Y%m%d') def remove_tree(top):
print "\n" + r"Output will be created in \pyfpdb\ and \fpdb_"+today+'\\' # Delete everything reachable from the directory named in 'top',
#print "Enter value for XXX (any length): ", # the comma means no newline # assuming there are no symbolic links.
#xxx = sys.stdin.readline().rstrip() # CAUTION: This is dangerous! For example, if top == '/', it
dist_dirname = r'fpdb-' + today + '-exe' # could delete all your disk files.
dist_dir = r'..\fpdb-' + today + '-exe' # sc: Nicked this from somewhere, added the if statement to try
print # make it a bit safer
if top in ('build','dist',distdir) and os.path.basename(os.getcwd()) == 'windows':
#print "removing directory '"+top+"' ..."
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
os.rmdir(top)
# remove build and dist dirs if they exist def copy_tree(source,destination):
source = source.replace('\\', '\\\\')
destination = destination.replace('\\', '\\\\')
print "*** Copying " + source + " to " + destination + " ***"
shutil.copytree( source, destination )
def copy_file(source,destination):
source = source.replace('\\', '\\\\')
destination = destination.replace('\\', '\\\\')
print "*** Copying " + source + " to " + destination + " ***"
shutil.copy( source, destination )
fpdbver = '0.20.906'
distdir = r'fpdb-' + fpdbver
rootdir = r'../../' #cwd is normally /packaging/windows
pydir = rootdir+'pyfpdb/'
gfxdir = rootdir+'gfx/'
sys.path.append( pydir ) # allows fpdb modules to be found by options/includes below
print "\n" + r"Output will be created in "+distdir
print "*** Cleaning working folders ***"
test_and_remove('dist') test_and_remove('dist')
test_and_remove('build') test_and_remove('build')
test_and_remove('pyfpdb') test_and_remove(distdir)
test_and_remove(dist_dirname) print "*** Building now in dist folder ***"
origIsSystemDLL = py2exe.build_exe.isSystemDLL
py2exe.build_exe.isSystemDLL = isSystemDLL
setup( setup(
name = 'fpdb', name = 'fpdb',
description = 'Free Poker DataBase', description = 'Free Poker DataBase',
version = '0.20.903', version = fpdbver,
windows = [ {'script': 'fpdb.pyw', "icon_resources": [(1, "../gfx/fpdb_large_icon.ico")]}, windows = [ {'script': pydir+'fpdb.pyw', "icon_resources": [(1, gfxdir+"fpdb_large_icon.ico")]},
{'script': 'HUD_main.pyw', }, {'script': pydir+'HUD_main.pyw', },
{'script': 'Configuration.py', } {'script': pydir+'Configuration.py', }
], ],
options = {'py2exe': { options = {'py2exe': {
@ -158,74 +152,56 @@ setup(
,'PartyPokerToFpdb', 'PokerStarsToFpdb' ,'PartyPokerToFpdb', 'PokerStarsToFpdb'
,'UltimateBetToFpdb', 'Win2dayToFpdb' ,'UltimateBetToFpdb', 'Win2dayToFpdb'
], ],
'excludes' : ['_tkagg', '_agg2', 'cocoaagg', 'fltkagg'], # surely we need this? '_gtkagg' 'excludes' : ['_tkagg', '_agg2', 'cocoaagg', 'fltkagg'],
'dll_excludes': ['libglade-2.0-0.dll', 'libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll' 'dll_excludes': ['libglade-2.0-0.dll', 'libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll'
, 'msvcr90.dll', 'MSVCP90.dll', 'MSVCR90.dll','msvcr90.dll'], # these are vis c / c++ runtimes, and must not be redistributed , 'msvcr90.dll', 'MSVCP90.dll', 'MSVCR90.dll','msvcr90.dll'], # these are vis c / c++ runtimes, and must not be redistributed
} }
}, },
# files in 2nd value in tuple are moved to dir named in 1st value # files in 2nd value in tuple are moved to dir named in 1st value
#data_files updated for new locations of licences + readme nolonger exists # this code will not walk a tree
data_files = [('', ['HUD_config.xml.example', 'Cards01.png', 'logging.conf', '../agpl-3.0.txt', '../fdl-1.2.txt', '../gpl-3.0.txt', '../gpl-2.0.txt', '../mit.txt', '../readme.txt']) # Note: cwd for 1st value is packaging/windows/dist (this is confusing BTW)
,(dist_dir, [r'..\run_fpdb.bat']) # Note: only include files here which are to be put into the package pyfpdb folder or subfolders
,( dist_dir + r'\gfx', glob.glob(r'..\gfx\*.*') )
# line below has problem with fonts subdir ('not a regular file') data_files = [('', glob.glob(rootdir+'*.txt'))
#,(r'matplotlibdata', glob.glob(r'c:\python25\Lib\site-packages\matplotlib\mpl-data\*')) ,('', [pydir+'HUD_config.xml.example',pydir+'Cards01.png', pydir+'logging.conf'])
] + matplotlib.get_py2exe_datafiles() ] + matplotlib.get_py2exe_datafiles()
) )
# rename completed output package as pyfpdb # ,(distdir, [rootdir+'run_fpdb.bat'])
os.rename('dist', 'pyfpdb') # ,(distdir+r'\gfx', glob.glob(gfxdir+'*.*'))
# ] +
print "*** py2exe build phase complete ***"
# pull pytz zoneinfo into pyfpdb package folder # copy zone info and fpdb translation folders
src_dir = r'c:\python26\Lib\site-packages\pytz\zoneinfo' copy_tree (r'c:\python26\Lib\site-packages\pytz\zoneinfo', os.path.join(r'dist', 'zoneinfo'))
src_dir = src_dir.replace('\\', '\\\\') copy_tree (pydir+r'locale', os.path.join(r'dist', 'locale'))
dest_dir = os.path.join(r'pyfpdb', 'zoneinfo')
shutil.copytree( src_dir, dest_dir )
# shunt pyfpdb package over to the distribution folder # create distribution folder and populate with gfx + bat
dest = os.path.join(dist_dirname, 'pyfpdb') copy_tree (gfxdir, os.path.join(distdir, 'gfx'))
# print "try renaming pyfpdb to", dest copy_file (rootdir+'run_fpdb.bat', distdir)
dest = dest.replace('\\', '\\\\')
# print "dest is now", dest
os.rename( 'pyfpdb', dest )
# prompt for gtk location print "*** Renaming dist folder as distribution pyfpdb folder ***"
dest = os.path.join(distdir, 'pyfpdb')
os.rename( 'dist', dest )
print "*** copying GTK runtime ***"
gtk_dir = "" gtk_dir = ""
while not os.path.exists(gtk_dir): while not os.path.exists(gtk_dir):
print "Enter directory name for GTK (e.g. c:\code\gtk_2.14.7-20090119)\n: ", # the comma means no newline print "Enter directory name for GTK (e.g. c:/gtk) : ", # the comma means no newline
gtk_dir = sys.stdin.readline().rstrip() gtk_dir = sys.stdin.readline().rstrip()
print "\ncopying files and dirs from ", gtk_dir, "to", dest.replace('\\\\', '\\'), "..." print "*** copying GTK runtime ***"
src = os.path.join(gtk_dir, 'bin', 'libgdk-win32-2.0-0.dll') dest = os.path.join(distdir, 'pyfpdb')
src = src.replace('\\', '\\\\') copy_file(os.path.join(gtk_dir, 'bin', 'libgdk-win32-2.0-0.dll'), dest )
shutil.copy( src, dest ) copy_file(os.path.join(gtk_dir, 'bin', 'libgobject-2.0-0.dll'), dest)
copy_tree(os.path.join(gtk_dir, 'etc'), os.path.join(dest, 'etc'))
copy_tree(os.path.join(gtk_dir, 'lib'), os.path.join(dest, 'lib'))
copy_tree(os.path.join(gtk_dir, 'share'), os.path.join(dest, 'share'))
src = os.path.join(gtk_dir, 'bin', 'libgobject-2.0-0.dll') print "*** All done! ***"
src = src.replace('\\', '\\\\') test_and_remove('build')
shutil.copy( src, dest ) print distdir+" is in the pyfpdb dir"
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 )
print "\nIf py2exe was successful you should now have a new dir"
print dist_dirname+" in your pyfpdb dir"
print """ print """
The following dirs can probably removed to make the final package smaller: The following dirs can probably removed to make the final package smaller:
@ -243,5 +219,3 @@ pyfpdb/share/themes/Default
Use 7-zip to zip up the distribution and create a self extracting archive and that's it! Use 7-zip to zip up the distribution and create a self extracting archive and that's it!
""" """

View File

@ -450,7 +450,6 @@ def sss():
self.settings['os']="windows" self.settings['os']="windows"
self.settings.update(self.config.get_db_parameters()) self.settings.update(self.config.get_db_parameters())
self.settings.update(self.config.get_tv_parameters())
self.settings.update(self.config.get_import_parameters()) self.settings.update(self.config.get_import_parameters())
self.settings.update(self.config.get_default_paths()) self.settings.update(self.config.get_default_paths())

View File

@ -44,9 +44,8 @@ class Betfair(HandHistoryConverter):
siteId = 7 # Needs to match id entry in Sites database siteId = 7 # Needs to match id entry in Sites database
# Static regexes # Static regexes
#re_SplitHands = re.compile(r'\n\n+') # Betfair 1.0 version
re_GameInfo = re.compile("^(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAME>(Texas Hold\'em|Omaha Hi|Razz))", re.MULTILINE) re_GameInfo = re.compile("^(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAME>(Texas Hold\'em|Omaha Hi|Razz))", re.MULTILINE)
re_SplitHands = re.compile(r'End of hand .{2}-\d{7,9}-\d+ \*\*\*\*\*\n') re_SplitHands = re.compile(r'\n\n+')
re_HandInfo = re.compile("\*\*\*\*\* Betfair Poker Hand History for Game (?P<HID>[0-9]+) \*\*\*\*\*\n(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>(Texas Hold\'em|Omaha Hi|Razz)) - (?P<DATETIME>[a-zA-Z]+, [a-zA-Z]+ \d+, \d\d:\d\d:\d\d GMT \d\d\d\d)\nTable (?P<TABLE>[ a-zA-Z0-9]+) \d-max \(Real Money\)\nSeat (?P<BUTTON>[0-9]+)", re.MULTILINE) re_HandInfo = re.compile("\*\*\*\*\* Betfair Poker Hand History for Game (?P<HID>[0-9]+) \*\*\*\*\*\n(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>(Texas Hold\'em|Omaha Hi|Razz)) - (?P<DATETIME>[a-zA-Z]+, [a-zA-Z]+ \d+, \d\d:\d\d:\d\d GMT \d\d\d\d)\nTable (?P<TABLE>[ a-zA-Z0-9]+) \d-max \(Real Money\)\nSeat (?P<BUTTON>[0-9]+)", re.MULTILINE)
re_Button = re.compile(ur"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE) re_Button = re.compile(ur"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE)
re_PlayerInfo = re.compile("Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*)\s\(\s(\$(?P<CASH>[.0-9]+)) \)") re_PlayerInfo = re.compile("Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*)\s\(\s(\$(?P<CASH>[.0-9]+)) \)")

View File

@ -110,7 +110,7 @@ def get_config(file_name, fallback = True):
example_path = '/usr/share/python-fpdb/' + file_name + '.example' example_path = '/usr/share/python-fpdb/' + file_name + '.example'
try: try:
shutil.copyfile(example_path, config_path) shutil.copyfile(example_path, config_path)
msg = 'Configuration file created: %s\n' % config_path msg = _("Config file has been created at %s.\n") % config_path
logging.info(msg) logging.info(msg)
return (config_path,False) return (config_path,False)
except IOError: except IOError:
@ -256,6 +256,20 @@ class Layout:
return temp + "\n" return temp + "\n"
class Email:
def __init__(self, node):
self.node = node
self.host= node.getAttribute("host")
self.username = node.getAttribute("username")
self.password = node.getAttribute("password")
self.useSsl = node.getAttribute("useSsl")
self.folder = node.getAttribute("folder")
self.fetchType = node.getAttribute("fetchType")
def __str__(self):
return " fetchType=%s\n host = %s\n username = %s\n password = %s\n useSsl = %s\n folder = %s\n" \
% (self.fetchType, self.host, self.username, self.password, self.useSsl, self.folder)
class Site: class Site:
def __init__(self, node): def __init__(self, node):
def normalizePath(path): def normalizePath(path):
@ -284,6 +298,7 @@ class Site:
self.xshift = node.getAttribute("xshift") self.xshift = node.getAttribute("xshift")
self.yshift = node.getAttribute("yshift") self.yshift = node.getAttribute("yshift")
self.layout = {} self.layout = {}
self.emails = {}
print _("Loading site"), self.site_name print _("Loading site"), self.site_name
@ -291,6 +306,10 @@ class Site:
lo = Layout(layout_node) lo = Layout(layout_node)
self.layout[lo.max] = lo self.layout[lo.max] = lo
for email_node in node.getElementsByTagName('email'):
email = Email(email_node)
self.emails[email.fetchType] = email
# Site defaults # Site defaults
self.xpad = 1 if self.xpad == "" else int(self.xpad) self.xpad = 1 if self.xpad == "" else int(self.xpad)
self.ypad = 0 if self.ypad == "" else int(self.ypad) self.ypad = 0 if self.ypad == "" else int(self.ypad)
@ -467,21 +486,6 @@ class Import:
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \ return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
% (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.fastStoreHudCache) % (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.fastStoreHudCache)
class Email:
def __init__(self, node):
self.node = node
self.host= node.getAttribute("host")
self.username = node.getAttribute("username")
self.password = node.getAttribute("password")
self.useSsl = node.getAttribute("useSsl")
self.folder = node.getAttribute("folder")
self.siteName = node.getAttribute("siteName")
self.fetchType = node.getAttribute("fetchType")
def __str__(self):
return " siteName=%s\n fetchType=%s\n host = %s\n username = %s\n password = %s\n useSsl = %s\n folder = %s\n" \
% (self.siteName, self.fetchType, self.host, self.username, self.password, self.useSsl, self.folder)
class HudUI: class HudUI:
def __init__(self, node): def __init__(self, node):
self.node = node self.node = node
@ -504,16 +508,6 @@ class HudUI:
return " label = %s\n" % self.label return " label = %s\n" % self.label
class Tv:
def __init__(self, node):
self.combinedStealFold = string_to_bool(node.getAttribute("combinedStealFold"), default=True)
self.combined2B3B = string_to_bool(node.getAttribute("combined2B3B"), default=True)
self.combinedPostflop = string_to_bool(node.getAttribute("combinedPostflop"), default=True)
def __str__(self):
return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" %
(self.combinedStealFold, self.combined2B3B, self.combinedPostflop) )
class General(dict): class General(dict):
def __init__(self): def __init__(self):
super(General, self).__init__() super(General, self).__init__()
@ -526,6 +520,20 @@ class General(dict):
log.debug(_("config.general: adding %s = %s") % (name,value)) log.debug(_("config.general: adding %s = %s") % (name,value))
self[name] = value self[name] = value
try:
self["version"]=int(self["version"])
except KeyError:
self["version"]=0
self["ui_language"]="system"
self["config_difficulty"]="expert"
def get_defaults(self):
self["version"]=0
self["ui_language"]="system"
self["config_difficulty"]="expert"
self["config_wrap_len"]="-1"
self["day_start"]="5"
def __str__(self): def __str__(self):
s = "" s = ""
for k in self: for k in self:
@ -600,6 +608,58 @@ class GUICashStats(list):
# s = s + " %s = %s\n" % (k, self[k]) # s = s + " %s = %s\n" % (k, self[k])
# return(s) # return(s)
class RawHands:
def __init__(self, node=None):
if node==None:
self.save="error"
self.compression="none"
print _("missing config section raw_hands")
else:
save=node.getAttribute("save")
if save in ("none", "error", "all"):
self.save=save
else:
print _("Invalid config value for raw_hands.save, defaulting to \"error\"")
self.save="error"
compression=node.getAttribute("compression")
if save in ("none", "gzip", "bzip2"):
self.compression=compression
else:
print _("Invalid config value for raw_hands.compression, defaulting to \"none\"")
self.compression="none"
#end def __init__
def __str__(self):
return " save= %s, compression= %s\n" % (self.save, self.compression)
#end class RawHands
class RawTourneys:
def __init__(self, node=None):
if node==None:
self.save="error"
self.compression="none"
print _("missing config section raw_tourneys")
else:
save=node.getAttribute("save")
if save in ("none", "error", "all"):
self.save=save
else:
print _("Invalid config value for raw_tourneys.save, defaulting to \"error\"")
self.save="error"
compression=node.getAttribute("compression")
if save in ("none", "gzip", "bzip2"):
self.compression=compression
else:
print _("Invalid config value for raw_tourneys.compression, defaulting to \"none\"")
self.compression="none"
#end def __init__
def __str__(self):
return " save= %s, compression= %s\n" % (self.save, self.compression)
#end class RawTourneys
class Config: class Config:
def __init__(self, file = None, dbname = ''): def __init__(self, file = None, dbname = ''):
# "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
@ -653,11 +713,12 @@ class Config:
self.hhcs = {} self.hhcs = {}
self.popup_windows = {} self.popup_windows = {}
self.db_selected = None # database the user would like to use self.db_selected = None # database the user would like to use
self.tv = None
self.general = General() self.general = General()
self.emails = {} self.emails = {}
self.gui_cash_stats = GUICashStats() self.gui_cash_stats = GUICashStats()
if doc.getElementsByTagName("general") == []:
self.general.get_defaults()
for gen_node in doc.getElementsByTagName("general"): for gen_node in doc.getElementsByTagName("general"):
self.general.add_elements(node=gen_node) # add/overwrite elements in self.general self.general.add_elements(node=gen_node) # add/overwrite elements in self.general
@ -716,18 +777,10 @@ class Config:
imp = Import(node = imp_node) imp = Import(node = imp_node)
self.imp = imp self.imp = imp
for email_node in doc.getElementsByTagName("email"):
email = Email(node = email_node)
if email.siteName!="": #FIXME: Why on earth is this needed?
self.emails[email.siteName+"_"+email.fetchType]=email
for hui_node in doc.getElementsByTagName('hud_ui'): for hui_node in doc.getElementsByTagName('hud_ui'):
hui = HudUI(node = hui_node) hui = HudUI(node = hui_node)
self.ui = hui self.ui = hui
for tv_node in doc.getElementsByTagName("tv"):
self.tv = Tv(node = tv_node)
db = self.get_db_parameters() 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()
@ -740,7 +793,18 @@ class Config:
db_pass = df_parms['db-password']) db_pass = df_parms['db-password'])
self.save(file=os.path.join(self.default_config_path, "HUD_config.xml")) self.save(file=os.path.join(self.default_config_path, "HUD_config.xml"))
if doc.getElementsByTagName("raw_hands") == []:
self.raw_hands = RawHands()
for raw_hands_node in doc.getElementsByTagName('raw_hands'):
self.raw_hands = RawHands(raw_hands_node)
if doc.getElementsByTagName("raw_tourneys") == []:
self.raw_tourneys = RawTourneys()
for raw_tourneys_node in doc.getElementsByTagName('raw_tourneys'):
self.raw_tourneys = RawTourneys(raw_tourneys_node)
print "" print ""
#end def __init__
def set_hhArchiveBase(self, path): def set_hhArchiveBase(self, path):
self.imp.node.setAttribute("hhArchiveBase", path) self.imp.node.setAttribute("hhArchiveBase", path)
@ -767,9 +831,12 @@ class Config:
return site_node return site_node
def getEmailNode(self, siteName, fetchType): def getEmailNode(self, siteName, fetchType):
for emailNode in self.doc.getElementsByTagName("email"): siteNode = self.get_site_node(siteName)
if emailNode.getAttribute("siteName") == siteName and emailNode.getAttribute("fetchType") == fetchType: for emailNode in siteNode.getElementsByTagName("email"):
if emailNode.getAttribute("fetchType") == fetchType:
print "found emailNode"
return emailNode return emailNode
break
#end def getEmailNode #end def getEmailNode
def getGameNode(self,gameName): def getGameNode(self,gameName):
@ -1010,15 +1077,6 @@ class Config:
return site_name return site_name
return None return None
def get_tv_parameters(self):
if self.tv is not None:
return {
'combinedStealFold': self.tv.combinedStealFold,
'combined2B3B': self.tv.combined2B3B,
'combinedPostflop': self.tv.combinedPostflop
}
return {}
# Allow to change the menu appearance # Allow to change the menu appearance
def get_hud_ui_parameters(self): def get_hud_ui_parameters(self):
hui = {} hui = {}
@ -1311,15 +1369,10 @@ if __name__== "__main__":
print c.imp print c.imp
print "----------- END IMPORT -----------" print "----------- END IMPORT -----------"
print "\n----------- TABLE VIEW -----------"
# print c.tv
print "----------- END TABLE VIEW -----------"
c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) )) c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) ))
c.save(file="testout.xml") c.save(file="testout.xml")
print "db = ", c.get_db_parameters() print "db = ", c.get_db_parameters()
# print "tv = ", c.get_tv_parameters()
# print "imp = ", c.get_import_parameters() # print "imp = ", c.get_import_parameters()
print "paths = ", c.get_default_paths("PokerStars") print "paths = ", c.get_default_paths("PokerStars")
print "colors = ", c.get_default_colors("PokerStars") print "colors = ", c.get_default_colors("PokerStars")

View File

@ -321,6 +321,11 @@ class Database:
else: else:
for row in rows: for row in rows:
for columnNumber in range(len(columnNames)): for columnNumber in range(len(columnNames)):
if columnNames[columnNumber][0]=="importTime":
result+=(" "+columnNames[columnNumber][0]+"=ignore\n")
elif columnNames[columnNumber][0]=="styleKey":
result+=(" "+columnNames[columnNumber][0]+"=ignore\n")
else:
result+=(" "+columnNames[columnNumber][0]+"="+str(row[columnNumber])+"\n") result+=(" "+columnNames[columnNumber][0]+"="+str(row[columnNumber])+"\n")
result+="\n" result+="\n"
result+="\n" result+="\n"
@ -2062,8 +2067,9 @@ class Database:
#print "info that we use to get TT by detail:", hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'], hand.gametype['limitType'], hand.isKO, hand.isRebuy, hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix #print "info that we use to get TT by detail:", hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'], hand.gametype['limitType'], hand.isKO, hand.isRebuy, hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix
#print "the query:",self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder']) #print "the query:",self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder'])
cursor.execute (self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder']), cursor.execute (self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder']),
(hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'], hand.gametype['limitType'], hand.isKO, (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'],
hand.isRebuy, hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix) #TODO: add koamount hand.gametype['limitType'], hand.maxseats, hand.isKO,
hand.isRebuy, hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix)
) )
result=cursor.fetchone() result=cursor.fetchone()
#print "result of fetching TT by details:",result #print "result of fetching TT by details:",result
@ -2072,7 +2078,8 @@ class Database:
tourneyTypeId = result[0] tourneyTypeId = result[0]
except TypeError: #this means we need to create a new entry except TypeError: #this means we need to create a new entry
cursor.execute (self.sql.query['insertTourneyType'].replace('%s', self.sql.query['placeholder']), cursor.execute (self.sql.query['insertTourneyType'].replace('%s', self.sql.query['placeholder']),
(hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'], hand.gametype['limitType'], (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'],
hand.gametype['limitType'], hand.maxseats,
hand.buyInChips, hand.isKO, hand.koBounty, hand.isRebuy, hand.buyInChips, hand.isKO, hand.koBounty, hand.isRebuy,
hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix, hand.added, hand.addedCurrency) hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix, hand.added, hand.addedCurrency)
) )

View File

@ -208,8 +208,7 @@ class Fulltilt(HandHistoryConverter):
if m is None: if m is None:
logging.info("Didn't match re_HandInfo") logging.info("Didn't match re_HandInfo")
logging.info(hand.handText) logging.info(hand.handText)
# Should this throw an exception? - CG raise FpdbParseError("No match in readHandInfo.")
return None
hand.handid = m.group('HID') hand.handid = m.group('HID')
hand.tablename = m.group('TABLE') hand.tablename = m.group('TABLE')
@ -260,8 +259,8 @@ class Fulltilt(HandHistoryConverter):
hand.isRebuy = True hand.isRebuy = True
if special == "KO": if special == "KO":
hand.isKO = True hand.isKO = True
if special == "Head's Up": if special == "Head's Up" or special == "Heads Up":
hand.maxSeats = 2 hand.maxseats = 2
if re.search("Matrix", special): if re.search("Matrix", special):
hand.isMatrix = True hand.isMatrix = True
if special == "Shootout": if special == "Shootout":

View File

@ -334,7 +334,6 @@ if __name__== "__main__":
else: settings['os'] = 'linuxmac' else: settings['os'] = 'linuxmac'
settings.update(config.get_db_parameters('fpdb')) settings.update(config.get_db_parameters('fpdb'))
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

@ -294,7 +294,7 @@ class GuiBulkImport():
self.cb_drophudcache.show() self.cb_drophudcache.show()
# button - Import # button - Import
self.load_button = gtk.Button(_('Import')) # todo: rename variables to import too self.load_button = gtk.Button(_('_Bulk Import')) # todo: rename variables to import too
self.load_button.connect('clicked', self.load_clicked, self.load_button.connect('clicked', self.load_clicked,
_('Import clicked')) _('Import clicked'))
self.table.attach(self.load_button, 2, 3, 4, 5, xpadding=0, ypadding=0, self.table.attach(self.load_button, 2, 3, 4, 5, xpadding=0, ypadding=0,
@ -378,7 +378,6 @@ def main(argv=None):
else: settings['os'] = 'linuxmac' else: settings['os'] = 'linuxmac'
settings.update(config.get_db_parameters()) settings.update(config.get_db_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

@ -72,19 +72,14 @@ class GuiImapFetcher (threading.Thread):
siteName=columns[0].get_text() siteName=columns[0].get_text()
fetchType=columns[1].get_text() fetchType=columns[1].get_text()
code=siteName+"_"+fetchType
for email in self.config.emails: toSave=self.config.supported_sites[siteName].emails[fetchType]
toSave=self.config.emails[email]
break
toSave.siteName=siteName
toSave.fetchType=fetchType
toSave.host=columns[2].get_text() toSave.host=columns[2].get_text()
toSave.username=columns[3].get_text() toSave.username=columns[3].get_text()
if columns[4].get_text()=="***": if columns[4].get_text()=="***":
toSave.password=self.passwords[code] toSave.password=self.passwords[siteName+fetchType]
else: else:
toSave.password=columns[4].get_text() toSave.password=columns[4].get_text()
@ -101,9 +96,10 @@ class GuiImapFetcher (threading.Thread):
def importAllClicked(self, widget, data=None): def importAllClicked(self, widget, data=None):
self.statusLabel.set_label(_("Starting import. Please wait.")) #FIXME: why doesnt this one show? self.statusLabel.set_label(_("Starting import. Please wait.")) #FIXME: why doesnt this one show?
for email in self.config.emails: for siteName in self.config.supported_sites:
for fetchType in self.config.supported_sites[siteName].emails:
try: try:
result=ImapFetcher.run(self.config.emails[email], self.db) result=ImapFetcher.run(self.config.supported_sites[siteName].emails[fetchType], self.db)
self.statusLabel.set_label(_("Finished import without error.")) self.statusLabel.set_label(_("Finished import without error."))
except IMAP4.error as error: except IMAP4.error as error:
if str(error)=="[AUTHENTICATIONFAILED] Authentication failed.": if str(error)=="[AUTHENTICATIONFAILED] Authentication failed.":
@ -128,11 +124,12 @@ class GuiImapFetcher (threading.Thread):
self.rowVBox = gtk.VBox() self.rowVBox = gtk.VBox()
self.mainVBox.add(self.rowVBox) self.mainVBox.add(self.rowVBox)
for email in self.config.emails: for siteName in self.config.supported_sites:
config=self.config.emails[email] for fetchType in self.config.supported_sites[siteName].emails:
config=self.config.supported_sites[siteName].emails[fetchType]
box=gtk.HBox(homogeneous=True) box=gtk.HBox(homogeneous=True)
for field in (config.siteName, config.fetchType): for field in (siteName, config.fetchType):
label=gtk.Label(field) label=gtk.Label(field)
box.add(label) box.add(label)
@ -142,7 +139,7 @@ class GuiImapFetcher (threading.Thread):
box.add(entry) box.add(entry)
entry=gtk.Entry() entry=gtk.Entry()
self.passwords[email]=config.password self.passwords[siteName+fetchType]=config.password
entry.set_text("***") entry.set_text("***")
box.add(entry) box.add(entry)
@ -153,7 +150,10 @@ class GuiImapFetcher (threading.Thread):
sslBox = gtk.combo_box_new_text() sslBox = gtk.combo_box_new_text()
sslBox.append_text(_("Yes")) sslBox.append_text(_("Yes"))
sslBox.append_text(_("No")) sslBox.append_text(_("No"))
if config.useSsl:
sslBox.set_active(0) sslBox.set_active(0)
else:
sslBox.set_active(1)
box.add(sslBox) box.add(sslBox)
#TODO: "run just this one" button #TODO: "run just this one" button

View File

@ -53,7 +53,6 @@ class GuiPositionalStats (threading.Thread):
settings = {} settings = {}
settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_db_parameters())
settings.update(self.conf.get_tv_parameters())
settings.update(self.conf.get_import_parameters()) settings.update(self.conf.get_import_parameters())
settings.update(self.conf.get_default_paths()) settings.update(self.conf.get_default_paths())

View File

@ -69,7 +69,6 @@ class GuiRingPlayerStats (GuiPlayerStats.GuiPlayerStats):
settings = {} settings = {}
settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_db_parameters())
settings.update(self.conf.get_tv_parameters())
settings.update(self.conf.get_import_parameters()) settings.update(self.conf.get_import_parameters())
settings.update(self.conf.get_default_paths()) settings.update(self.conf.get_default_paths())

View File

@ -82,7 +82,6 @@ class GuiSessionViewer (threading.Thread):
settings = {} settings = {}
settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_db_parameters())
settings.update(self.conf.get_tv_parameters())
settings.update(self.conf.get_import_parameters()) settings.update(self.conf.get_import_parameters())
settings.update(self.conf.get_default_paths()) settings.update(self.conf.get_default_paths())

View File

@ -5,8 +5,11 @@
<!-- config_wrap_len is preferred max line length in this file, -1 means no max <!-- config_wrap_len is preferred max line length in this file, -1 means no max
day_start is time that logical day starts, e.g. 5 means that any play day_start is time that logical day starts, e.g. 5 means that any play
between 00:00 and 04:59:59 counts as being on the previous day --> between 00:00 and 04:59:59 counts as being on the previous day -->
<general config_wrap_len="-1" <general version="1"
config_wrap_len="-1"
day_start="5" day_start="5"
ui_language="system"
config_difficulty="expert"
/> />
<import callFpdbHud = "True" interval = "10" fastStoreHudCache="False" hhArchiveBase="~/.fpdb/HandHistories/" saveActions="True"></import> <import callFpdbHud = "True" interval = "10" fastStoreHudCache="False" hhArchiveBase="~/.fpdb/HandHistories/" saveActions="True"></import>
@ -156,6 +159,7 @@ Left-Drag to Move"
xshift="0" xshift="0"
yshift="0" yshift="0"
supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo"> supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<email fetchType="request-summary" host="YOUR_EMAIL_SERVER" username="YOUR_EMAIL_USERNAME" password="YOUR_EMAIL_PASSWORD" useSsl="True" folder="INBOX"/>
<layout max="8" width="792" height="546" fav_seat="0"> <layout max="8" width="792" height="546" fav_seat="0">
<location seat="1" x="684" y="61"> </location> <location seat="1" x="684" y="61"> </location>
<location seat="2" x="689" y="239"> </location> <location seat="2" x="689" y="239"> </location>
@ -439,16 +443,7 @@ Left-Drag to Move"
</layout> </layout>
</site> </site>
<site HH_path="C:/Program Files/Carbon Poker/HandHistory/YOUR SCREEN NAME HERE/" converter="CarbonToFpdb" decoder="everleaf_decode_table" enabled="True" screen_name="YOUR SCREEN NAME HERE" site_name="Carbon" site_path="C:/Program Files/Carbin/" supported_games="holdem" table_finder="Carbon Poker.exe">
<site enabled="False"
site_name="Betfair"
table_finder="Betfair Poker.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path="C:/Program Files/Betfair/Betfair Poker/"
HH_path="C:/Program Files/Betfair/Betfair Poker/HandHistory/YOUR SCREEN NAME HERE/"
decoder="everleaf_decode_table"
converter="BetfairToFpdb"
supported_games="holdem">
<layout fav_seat="0" height="547" max="8" width="794"> <layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location> <location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location> <location seat="2" x="650" y="230"> </location>
@ -484,8 +479,7 @@ Left-Drag to Move"
</layout> </layout>
</site> </site>
<site HH_path="C:/Program Files/OnGame Sking/HandHistory/YOUR SCREEN NAME HERE/" converter="OnGameToFpdb" decoder="everleaf_decode_table" enabled="False" screen_name="YOUR SCREEN NAME HERE" site_name="OnGame" site_path="C:/Program Files/OnGame/" supported_games="holdem" table_finder="OnGame.exe">
<site HH_path="C:/Program Files/Carbon Poker/HandHistory/YOUR SCREEN NAME HERE/" converter="CarbonToFpdb" decoder="everleaf_decode_table" enabled="True" screen_name="YOUR SCREEN NAME HERE" site_name="Carbon" site_path="C:/Program Files/Carbin/" supported_games="holdem" table_finder="Carbon Poker.exe">
<layout fav_seat="0" height="547" max="8" width="794"> <layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location> <location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location> <location seat="2" x="650" y="230"> </location>
@ -673,45 +667,14 @@ Left-Drag to Move"
<hhc site="OnGame" converter="OnGameToFpdb"/> <hhc site="OnGame" converter="OnGameToFpdb"/>
</hhcs> </hhcs>
<!-- attribute names chosen to be in alphabetic order so that order can be set when retrieved --> <raw_hands save="none" compression="none"/>
<gui_cash_stats>
<col col_name="game" col_title="Game" disp_all="True" disp_posn="True" field_format="%s" field_type="str" xalignment="0.0"/> <raw_tourneys save="none" compression="none"/>
<col col_name="hand" col_title="Hand" disp_all="False" disp_posn="False" field_format="%s" field_type="str" xalignment="0.0"/>
<col col_name="plposition" col_title="Posn" disp_all="False" disp_posn="False" field_format="%s" field_type="str" xalignment="1.0"/>
<col col_name="pname" col_title="Name" disp_all="False" disp_posn="False" field_format="%s" field_type="str" xalignment="0.0"/>
<col col_name="n" col_title="Hds" disp_all="True" disp_posn="True" field_format="%1.0f" field_type="str" xalignment="1.0"/>
<col col_name="avgseats" col_title="Seats" disp_all="False" disp_posn="False" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="vpip" col_title="VPIP" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="pfr" col_title="PFR" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="pf3" col_title="PF3" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="aggfac" col_title="AggFac" disp_all="True" disp_posn="True" field_format="%2.2f" field_type="str" xalignment="1.0"/>
<col col_name="aggfrq" col_title="AggFreq" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="conbet" col_title="ContBet" disp_all="False" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="rfi" col_title="RFI" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="steals" col_title="Steals" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="saw_f" col_title="Saw_F" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="sawsd" col_title="SawSD" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="wtsdwsf" col_title="WtSDwsF" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="wmsd" col_title="W$SD" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="flafq" col_title="FlAFq" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="tuafq" col_title="TuAFq" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="rvafq" col_title="RvAFq" disp_all="True" disp_posn="True" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="pofafq" col_title="PoFAFq" disp_all="False" disp_posn="False" field_format="%3.1f" field_type="str" xalignment="1.0"/>
<col col_name="net" col_title="Net($)" disp_all="True" disp_posn="True" field_format="%6.2f" field_type="cash" xalignment="1.0"/>
<col col_name="bbper100" col_title="bb/100" disp_all="True" disp_posn="True" field_format="%4.2f" field_type="str" xalignment="1.0"/>
<col col_name="rake" col_title="Rake($)" disp_all="True" disp_posn="True" field_format="%6.2f" field_type="cash" xalignment="1.0"/>
<col col_name="bb100xr" col_title="bbxr/100" disp_all="True" disp_posn="True" field_format="%4.2f" field_type="str" xalignment="1.0"/>
<col col_name="variance" col_title="Variance" disp_all="True" disp_posn="True" field_format="%5.2f" field_type="str" xalignment="1.0"/>
</gui_cash_stats>
<supported_databases> <supported_databases>
<!-- <database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD"></database> --> <!-- <database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD"></database> -->
<database db_ip="localhost" db_server="sqlite" db_name="fpdb.db3" db_user="fpdb" db_pass="fpdb"/> <database db_ip="localhost" db_server="sqlite" db_name="fpdb.db3" db_user="fpdb" db_pass="fpdb"/>
</supported_databases> </supported_databases>
<email>
<email siteName="PokerStars" fetchType="request-summary" host="YOUR_EMAIL_SERVER" username="YOUR_EMAIL_USERNAME" password="YOUR_EMAIL_PASSWORD" useSsl="True" folder="INBOX"/>
</email>
</FreePokerToolsConfig> </FreePokerToolsConfig>
<!-- IMPORTANT: Please note that fpdb stores your email password in clear text. <!-- IMPORTANT: Please note that fpdb stores your email password in clear text.
So do not post a config containing a password on the Internet or anywhere else without removing the password! --> So do not post a config containing a password on the Internet or anywhere else without removing the password! -->

View File

@ -58,7 +58,7 @@ class Hand(object):
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'} LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''} SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'} MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9, 'Partouche':10, 'Carbon':11 } SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9, 'Partouche':10, 'Carbon':11, 'PKR':12 }
def __init__(self, config, sitename, gametype, handText, builtFrom = "HHC"): def __init__(self, config, sitename, gametype, handText, builtFrom = "HHC"):
@ -320,10 +320,8 @@ If a player has None chips he won't be added."""
def checkPlayerExists(self,player): def checkPlayerExists(self,player):
if player not in [p[1] for p in self.players]: if player not in [p[1] for p in self.players]:
print _("DEBUG: checkPlayerExists %s fail") % player print (_("DEBUG: checkPlayerExists %s fail on hand number %s") % (player, self.handid))
raise FpdbParseError(_("checkPlayerExists: '%s' failed.") % player) raise FpdbParseError(_("checkPlayerExists: '%s fail on hand number %s") % (player, self.handid))
def setCommunityCards(self, street, cards): def setCommunityCards(self, street, cards):
log.debug("setCommunityCards %s %s" %(street, cards)) log.debug("setCommunityCards %s %s" %(street, cards))

View File

@ -70,6 +70,7 @@ class HandHistoryConverter():
# "utf_8" is more likely if there are funny characters # "utf_8" is more likely if there are funny characters
codepage = "cp1252" codepage = "cp1252"
re_tzOffset = re.compile('^\w+[+-]\d{4}$')
def __init__(self, config, in_path = '-', out_path = '-', follow=False, index=0, autostart=True, starsArchive=False, ftpArchive=False): def __init__(self, config, in_path = '-', out_path = '-', follow=False, index=0, autostart=True, starsArchive=False, ftpArchive=False):
"""\ """\
@ -150,6 +151,7 @@ Otherwise, finish at EOF.
log.debug(handText) log.debug(handText)
else: else:
handsList = self.allHandsAsList() handsList = self.allHandsAsList()
log.debug( _("handsList is ") + str(handsList) )
log.info("Parsing %d hands" % len(handsList)) log.info("Parsing %d hands" % len(handsList))
# Determine if we're dealing with a HH file or a Summary file # Determine if we're dealing with a HH file or a Summary file
# quick fix : empty files make the handsList[0] fail ==> If empty file, go on with HH parsing # quick fix : empty files make the handsList[0] fail ==> If empty file, go on with HH parsing
@ -568,17 +570,32 @@ or None if we fail to get the info """
@staticmethod @staticmethod
def changeTimezone(time, givenTimezone, wantedTimezone): def changeTimezone(time, givenTimezone, wantedTimezone):
#print "raw time:",time, "given TZ:", givenTimezone """Takes a givenTimezone in format AAA or AAA+HHMM where AAA is a standard timezone
and +HHMM is an optional offset (+/-) in hours (HH) and minutes (MM)
(See OnGameToFpdb.py for example use of the +HHMM part)
Tries to convert the time parameter (with no timezone) from the givenTimezone to
the wantedTimeZone (currently only allows "UTC")
"""
log.debug( _("raw time:")+str(time) + _(" given TZ:")+str(givenTimezone) )
if wantedTimezone=="UTC": if wantedTimezone=="UTC":
wantedTimezone = pytz.utc wantedTimezone = pytz.utc
else: else:
raise Error #TODO raise appropriate error raise Error #TODO raise appropriate error
givenTZ = None
if HandHistoryConverter.re_tzOffset.match(givenTimezone):
offset = int(givenTimezone[-5:])
givenTimezone = givenTimezone[0:-5]
log.debug( _("changeTimeZone: offset=") + str(offset) )
else: offset=0
if givenTimezone=="ET": if givenTimezone=="ET":
givenTimezone = timezone('US/Eastern') givenTZ = timezone('US/Eastern')
elif givenTimezone=="CET": elif givenTimezone=="CET":
givenTimezone = timezone('Europe/Berlin') givenTZ = timezone('Europe/Berlin')
#Note: Daylight Saving Time is standardised across the EU so this should be fine #Note: Daylight Saving Time is standardised across the EU so this should be fine
elif givenTimezone == 'GMT': # Greenwich Mean Time (same as UTC - no change to time)
givenTZ = timezone('GMT')
elif givenTimezone == 'HST': # Hawaiian Standard Time elif givenTimezone == 'HST': # Hawaiian Standard Time
pass pass
elif givenTimezone == 'AKT': # Alaska Time elif givenTimezone == 'AKT': # Alaska Time
@ -612,23 +629,27 @@ or None if we fail to get the info """
elif givenTimezone == 'JST': # Japan Standard Time elif givenTimezone == 'JST': # Japan Standard Time
pass pass
elif givenTimezone == 'AWST': # Australian Western Standard Time elif givenTimezone == 'AWST': # Australian Western Standard Time
givenTimezone = timezone('Australia/West') givenTZ = timezone('Australia/West')
elif givenTimezone == 'ACST': # Australian Central Standard Time elif givenTimezone == 'ACST': # Australian Central Standard Time
givenTimezone = timezone('Australia/Darwin') givenTZ = timezone('Australia/Darwin')
elif givenTimezone == 'AEST': # Australian Eastern Standard Time elif givenTimezone == 'AEST': # Australian Eastern Standard Time
# Each State on the East Coast has different DSTs. # Each State on the East Coast has different DSTs.
# Melbournce is out because I don't like AFL, Queensland doesn't have DST # Melbournce is out because I don't like AFL, Queensland doesn't have DST
# ACT is full of politicians and Tasmania will never notice. # ACT is full of politicians and Tasmania will never notice.
# Using Sydney. # Using Sydney.
givenTimezone = timezone('Australia/Sydney') givenTZ = timezone('Australia/Sydney')
elif givenTimezone == 'NZT': # New Zealand Time elif givenTimezone == 'NZT': # New Zealand Time
pass pass
else: else:
raise Error #TODO raise appropriate error raise Error #TODO raise appropriate error
localisedTime = givenTimezone.localize(time) if givenTZ is None:
utcTime = localisedTime.astimezone(wantedTimezone) raise Error #TODO raise appropriate error
#print "utcTime:",utcTime # (or just return time unchanged?)
localisedTime = givenTZ.localize(time)
utcTime = localisedTime.astimezone(wantedTimezone) + datetime.timedelta(seconds=-3600*(offset/100)-60*(offset%100))
log.debug( _("utcTime:")+str(utcTime) )
return utcTime return utcTime
#end @staticmethod def changeTimezone #end @staticmethod def changeTimezone

View File

@ -55,7 +55,6 @@ else:
def _(string): return string def _(string): return string
# FreePokerTools modules # FreePokerTools modules
import Tables # needed for testing only
import Configuration import Configuration
import Stats import Stats
import Mucked import Mucked
@ -950,26 +949,3 @@ class Popup_window:
# window.present() # window.present()
if __name__== "__main__":
main_window = gtk.Window()
main_window.connect("destroy", destroy)
label = gtk.Label(_('Fake main window, blah blah, blah\nblah, blah'))
main_window.add(label)
main_window.show_all()
c = Configuration.Config()
#tables = Tables.discover(c)
t = Tables.discover_table_by_name(c, "Corona")
if t is None:
print _("Table not found.")
db = Database.Database(c, 'fpdb', 'holdem')
stat_dict = db.get_stats_from_hand(1)
# for t in tables:
win = Hud(None, t, 10, 'holdem', c, db) # parent, table, max, poker_game, config, db_connection
win.create(1, c, stat_dict, None) # hand, config, stat_dict, cards):
# t.get_details()
win.update(8300, c) # self, hand, config):
gtk.main()

View File

@ -43,6 +43,8 @@ class OnGame(HandHistoryConverter):
codepage = ("utf8", "cp1252") codepage = ("utf8", "cp1252")
siteId = 5 # Needs to match id entry in Sites database siteId = 5 # Needs to match id entry in Sites database
mixes = { } # Legal mixed games
sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\xe2\x82\xac", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE
substitutions = { substitutions = {
'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes
'LS' : "\$|\xe2\x82\xac|" # legal currency symbols - Euro(cp1252, utf-8) 'LS' : "\$|\xe2\x82\xac|" # legal currency symbols - Euro(cp1252, utf-8)
@ -65,7 +67,8 @@ class OnGame(HandHistoryConverter):
#self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)') #self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
# Static regexes # Static regexes
re_SplitHands = re.compile(r'End of hand .{2}-\d{7,9}-\d+ \*\*\*\*\*\n') # ***** End of hand R5-75443872-57 *****
re_SplitHands = re.compile(u'\*\*\*\*\*\sEnd\sof\shand\s[-A-Z\d]+.*\n(?=\*)')
# ***** History for hand R5-75443872-57 ***** # ***** History for hand R5-75443872-57 *****
# Start hand: Wed Aug 18 19:29:10 GMT+0100 2010 # Start hand: Wed Aug 18 19:29:10 GMT+0100 2010
@ -73,29 +76,33 @@ class OnGame(HandHistoryConverter):
re_HandInfo = re.compile(u""" re_HandInfo = re.compile(u"""
\*\*\*\*\*\sHistory\sfor\shand\s(?P<HID>[-A-Z\d]+).* \*\*\*\*\*\sHistory\sfor\shand\s(?P<HID>[-A-Z\d]+).*
Start\shand:\s(?P<DATETIME>.*) Start\shand:\s(?P<DATETIME>.*)
Table:\s(?P<TABLE>[\'\w]+)\s\[\d+\]\s\( Table:\s(?P<TABLE>[\'\w\s]+)\s\[\d+\]\s\(
( (
(?P<LIMIT>No\sLimit|Limit|LIMIT|Pot\sLimit)\s (?P<LIMIT>NO_LIMIT|Limit|LIMIT|Pot\sLimit)\s
(?P<GAME>TEXAS_HOLDEM|RAZZ)\s (?P<GAME>TEXAS_HOLDEM|RAZZ)\s
(?P<SB>[.0-9]+)/ (?P<SB>[.0-9]+)/
(?P<BB>[.0-9]+) (?P<BB>[.0-9]+)
)? )?
""" % substitutions, re.MULTILINE|re.DOTALL|re.VERBOSE) """ % substitutions, re.MULTILINE|re.DOTALL|re.VERBOSE)
re_TailSplitHands = re.compile(u'(\*\*\*\*\*\sEnd\sof\shand\s[-A-Z\d]+.*\n)(?=\*)')
re_Button = re.compile('Button: seat (?P<BUTTON>\d+)', re.MULTILINE) # Button: seat 2
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
# Wed Aug 18 19:45:30 GMT+0100 2010 # Wed Aug 18 19:45:30 GMT+0100 2010
re_DateTime = re.compile(""" re_DateTime = re.compile("""
[a-zA-Z]{3}\s [a-zA-Z]{3}\s
(?P<M>[a-zA-Z]{3})\s (?P<M>[a-zA-Z]{3})\s
(?P<D>[0-9]{2})\s (?P<D>[0-9]{2})\s
(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)\sGMT (?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)\s
(?P<OFFSET>[-+]\d+)\s (?P<OFFSET>\w+[-+]\d+)\s
(?P<Y>[0-9]{4}) (?P<Y>[0-9]{4})
""", re.MULTILINE|re.VERBOSE) """, re.MULTILINE|re.VERBOSE)
# self.rexx.button_re = re.compile('#SUMMARY\nDealer: (?P<BUTTONPNAME>.*)\n') # self.rexx.button_re = re.compile('#SUMMARY\nDealer: (?P<BUTTONPNAME>.*)\n')
#Seat 1: .Lucchess ($4.17 in chips) #Seat 1: .Lucchess ($4.17 in chips)
re_PlayerInfo = re.compile(u'Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \((?P<CASH>[.0-9]+) \)') re_PlayerInfo = re.compile(u'Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \((?P<CASH>[.0-9]+)\)')
def compilePlayerRegexs(self, hand): def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players]) players = set([player[1] for player in hand.players])
@ -104,29 +111,30 @@ class OnGame(HandHistoryConverter):
# TODO: should probably rename re_HeroCards and corresponding method, # TODO: should probably rename re_HeroCards and corresponding method,
# since they are used to find all cards on lines starting with "Dealt to:" # since they are used to find all cards on lines starting with "Dealt to:"
# They still identify the hero. # They still identify the hero.
self.compiledPlayers = players
#ANTES/BLINDS #ANTES/BLINDS
#helander2222 posts blind ($0.25), lopllopl posts blind ($0.50). #helander2222 posts blind ($0.25), lopllopl posts blind ($0.50).
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")" player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
subst = {'PLYR': player_re, 'CUR': self.sym[hand.gametype['currency']]} subst = {'PLYR': player_re, 'CUR': self.sym[hand.gametype['currency']]}
re_PostSB = re.compile('(?P<PNAME>.*) posts blind \(\$?(?P<SB>[.0-9]+)\), ') self.re_PostSB = re.compile('(?P<PNAME>.*) posts small blind \(\$?(?P<SB>[.0-9]+)\)')
re_PostBB = re.compile('\), (?P<PNAME>.*) posts blind \(\$?(?P<BB>[.0-9]+)\).') self.re_PostBB = re.compile('\), (?P<PNAME>.*) posts big blind \(\$?(?P<BB>[.0-9]+)\)')
re_Antes = re.compile(r"^%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[.0-9]+)" % subst, re.MULTILINE) self.re_Antes = re.compile(r"^%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[.0-9]+)" % subst, re.MULTILINE)
re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst, re.MULTILINE) self.re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst, re.MULTILINE)
re_PostBoth = re.compile('.*\n(?P<PNAME>.*): posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)') self.re_PostBoth = re.compile('.*\n(?P<PNAME>.*): posts small \& big blinds \(\$? (?P<SBBB>[.0-9]+)\)')
re_HeroCards = re.compile('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<CARDS>.*) \]') self.re_HeroCards = re.compile('Dealing\sto\s%(PLYR)s:\s\[(?P<CARDS>.*)\]' % subst)
#lopllopl checks, Eurolll checks, .Lucchess checks. #lopllopl checks, Eurolll checks, .Lucchess checks.
re_Action = re.compile('(, )?(?P<PNAME>.*?)(?P<ATYPE> bets| checks| raises| calls| folds)( \$(?P<BET>\d*\.?\d*))?( and is all-in)?') self.re_Action = re.compile('(, )?(?P<PNAME>.*?)(?P<ATYPE> bets| checks| raises| calls| folds)( (?P<BET>\d*\.?\d*))?( and is all-in)?')
re_Board = re.compile(r"\[board cards (?P<CARDS>.+) \]") #self.re_Board = re.compile(r"\[board cards (?P<CARDS>.+) \]")
#Uchilka shows [ KC,JD ] #Uchilka shows [ KC,JD ]
re_ShowdownAction = re.compile('(?P<PNAME>.*) shows \[ (?P<CARDS>.+) \]') self.re_ShowdownAction = re.compile('(?P<PNAME>.*) shows \[ (?P<CARDS>.+) \]')
# TODO: read SUMMARY correctly for collected pot stuff. # TODO: read SUMMARY correctly for collected pot stuff.
#Uchilka, bets $11.75, collects $23.04, net $11.29 # Main pot: 6.75 won by player3 (6.45)
re_CollectPot = re.compile('(?P<PNAME>.*), bets.+, collects \$(?P<POT>\d*\.?\d*), net.* ') self.re_CollectPot = re.compile('Main pot: (?P<POT>\d*\.?\d*) won by %(PLYR)s' % subst)
re_sitsOut = re.compile('(?P<PNAME>.*) sits out') self.re_sitsOut = re.compile('(?P<PNAME>.*) sits out')
def readSupportedGames(self): def readSupportedGames(self):
return [ return [
@ -179,9 +187,11 @@ class OnGame(HandHistoryConverter):
# So we need to re-interpret te string to be useful # So we need to re-interpret te string to be useful
m1 = self.re_DateTime.finditer(info[key]) m1 = self.re_DateTime.finditer(info[key])
for a in m1: for a in m1:
datetimestr = "%s %s %s %s:%s:%s" % (a.group('M'),a.group('D'), a.group('Y'), a.group('H'),a.group('MIN'),a.group('S')) datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'),a.group('M'), a.group('D'), a.group('H'),a.group('MIN'),a.group('S'))
hand.startTime = time.strptime(datetimestr, "%b %d %Y %H:%M:%S") tzoffset = a.group('OFFSET')
# TODO: Manually adjust time against OFFSET # TODO: Manually adjust time against OFFSET
hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%b/%d %H:%M:%S") # also timezone at end, e.g. " ET"
hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, tzoffset, "UTC")
if key == 'HID': if key == 'HID':
hand.handid = info[key] hand.handid = info[key]
if key == 'TABLE': if key == 'TABLE':
@ -206,10 +216,10 @@ class OnGame(HandHistoryConverter):
#elif hand.gametype['base'] in ("stud"): #elif hand.gametype['base'] in ("stud"):
#elif hand.gametype['base'] in ("draw"): #elif hand.gametype['base'] in ("draw"):
# only holdem so far: # only holdem so far:
m = re.search(r"pocket cards(?P<PREFLOP>.+(?=flop)|.+(?=Summary))" m = re.search(r"pocket cards(?P<PREFLOP>.+(?= Dealing flop )|.+(?=Summary))"
r"(flop (?P<FLOP>\[\S\S, \S\S, \S\S\].+(?=turn)|.+(?=Summary)))?" r"( Dealing flop (?P<FLOP>\[\S\S, \S\S, \S\S\].+(?= Dealing turn)|.+(?=Summary)))?"
r"(turn (?P<TURN>\[\S\S, \S\S, \S\S\, \S\S\].+(?=river)|.+(?=Summary)))?" r"( Dealing turn (?P<TURN>\[\S\S\].+(?= Dealing river)|.+(?=Summary)))?"
r"(river (?P<RIVER>\[\S\S, \S\S, \S\S\, \S\S, \S\S\].+(?=Summary)))?", hand.handText, re.DOTALL) r"( Dealing river (?P<RIVER>\[\S\S\].+(?=Summary)))?", hand.handText, re.DOTALL)
hand.addStreets(m) hand.addStreets(m)
@ -224,17 +234,27 @@ class OnGame(HandHistoryConverter):
else: else:
log.info(_('readButton: not found')) log.info(_('readButton: not found'))
def readCommunityCards(self, hand, street): # def readCommunityCards(self, hand, street):
print hand.streets.group(street) # #print hand.streets.group(street)
# if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
# m = self.re_Board.search(hand.streets.group(street))
# hand.setCommunityCards(street, m.group('CARDS').split(','))
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
m = self.re_Board.search(hand.streets.group(street)) #print "DEBUG readCommunityCards:", street, hand.streets.group(street)
hand.setCommunityCards(street, m.group('CARDS').split(',')) m = self.re_Board.search(hand.streets[street])
hand.setCommunityCards(street, m.group('CARDS').split(', '))
def readBlinds(self, hand): def readBlinds(self, hand):
log.debug( _("readBlinds starting") )
try: try:
m = self.re_PostSB.search(hand.handText) m = self.re_PostSB.search(hand.handText)
if m is None:
log.debug( _("re_postSB failed, hand=") + hand.handText )
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB')) hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind except: # no small blind
log.debug( _("readBlinds in noSB exception")+str(sys.exc_info()) )
hand.addBlind(None, None, None) hand.addBlind(None, None, None)
for a in self.re_PostBB.finditer(hand.handText): for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
@ -255,23 +275,23 @@ class OnGame(HandHistoryConverter):
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN')) hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
def readHeroCards(self, hand): def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText) # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
if(m == None): # we need to grab hero's cards
#Not involved in hand for street in ('PREFLOP', 'DEAL'):
hand.involved = False if street in hand.streets.keys():
else: m = self.re_HeroCards.finditer(hand.streets[street])
hand.hero = m.group('PNAME') for found in m:
# "2c, qh" -> set(["2c","qc"]) hand.hero = found.group('PNAME')
# Also works with Omaha hands. newcards = found.group('CARDS').split(', ')
cards = m.group('CARDS') hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
cards = set(cards.split(','))
hand.addHoleCards(cards, m.group('PNAME'))
def readAction(self, hand, street): def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets.group(street)) m = self.re_Action.finditer(hand.streets[street])
for action in m: for action in m:
acts = action.groupdict()
#print "DEBUG: acts: %s" %acts
if action.group('ATYPE') == ' raises': if action.group('ATYPE') == ' raises':
hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' calls': elif action.group('ATYPE') == ' calls':
hand.addCall( street, action.group('PNAME'), action.group('BET') ) hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' bets': elif action.group('ATYPE') == ' bets':
@ -280,10 +300,12 @@ class OnGame(HandHistoryConverter):
hand.addFold( street, action.group('PNAME')) hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == ' checks': elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME')) hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == ' discards':
hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('DISCARDED'))
elif action.group('ATYPE') == ' stands pat':
hand.addStandsPat( street, action.group('PNAME'))
else: else:
print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),) print _("DEBUG: unimplemented readAction: '%s' '%s'") %(action.group('PNAME'),action.group('ATYPE'),)
#hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]]
# TODO: Everleaf does not record uncalled bets.
def readShowdownActions(self, hand): def readShowdownActions(self, hand):
for shows in self.re_ShowdownAction.finditer(hand.handText): for shows in self.re_ShowdownAction.finditer(hand.handText):

390
pyfpdb/PkrToFpdb.py Executable file
View File

@ -0,0 +1,390 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2010, Carl Gherardi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
import sys
from HandHistoryConverter import *
import locale
lang=locale.getdefaultlocale()[0][0:2]
if lang=="en":
def _(string): return string
else:
import gettext
try:
trans = gettext.translation("fpdb", localedir="locale", languages=[lang])
trans.install()
except IOError:
def _(string): return string
class Pkr(HandHistoryConverter):
# Class Variables
sitename = "PKR"
filetype = "text"
codepage = ("utf8", "cp1252")
siteId = 12 # Needs to match id entry in Sites database
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games
sym = {'USD': "\$"} # ADD Euro, Sterling, etc HERE
substitutions = {
'LEGAL_ISO' : "USD", # legal ISO currency codes
'LS' : "\$|" # legal currency symbols - Euro(cp1252, utf-8)
}
limits = { 'NO LIMIT':'nl', 'POT LIMIT':'pl', 'LIMIT':'fl' }
games = { # base, category
"HOLD'EM" : ('hold','holdem'),
'FIXMEOmaha' : ('hold','omahahi'),
'FIXMEOmaha Hi/Lo' : ('hold','omahahilo'),
'FIXME5 Card Draw' : ('draw','fivedraw')
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
# Static regexes
re_GameInfo = re.compile(u"""
Table\s\#\d+\s\-\s(?P<TABLE>[a-zA-Z\ \d]+)\s
Starting\sHand\s\#(?P<HID>[0-9]+)\s
Start\stime\sof\shand:\s(?P<DATETIME>.*)\s
Last\sHand\s\#[0-9]+\s
Game\sType:\s(?P<GAME>HOLD'EM|5\sCard\sDraw)\s
Limit\sType:\s(?P<LIMIT>NO\sLIMIT|LIMIT|POT\sLIMIT)\s
Table\sType\:\sRING\s
Money\sType:\sREAL\sMONEY\s
Blinds\sare\snow\s(?P<CURRENCY>%(LS)s|)?
(?P<SB>[.0-9]+)/(%(LS)s)?
(?P<BB>[.0-9]+)
""" % substitutions, re.MULTILINE|re.VERBOSE)
re_PlayerInfo = re.compile(u"""
^Seat\s(?P<SEAT>[0-9]+):\s
(?P<PNAME>.*)\s-\s
(%(LS)s)?(?P<CASH>[.0-9]+)
""" % substitutions, re.MULTILINE|re.VERBOSE)
re_HandInfo = re.compile("""
^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s
((?P<MAX>\d+)-max\s)?
(?P<PLAY>\(Play\sMoney\)\s)?
(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
re.MULTILINE|re.VERBOSE)
re_SplitHands = re.compile('\n\n+')
re_TailSplitHands = re.compile('(\n\n\n+)')
re_Button = re.compile('Seat #(?P<BUTTON>\d+) is the button', re.MULTILINE)
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
# self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
re_DateTime = re.compile("""(?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]+)""", re.MULTILINE)
def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs.
self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
subst = {'PLYR': player_re, 'CUR': self.sym[hand.gametype['currency']]}
log.debug("player_re: " + player_re)
self.re_PostSB = re.compile(r"^%(PLYR)s posts small blind %(CUR)s(?P<SB>[.0-9]+)" % subst, re.MULTILINE)
# FIXME: Sionel posts $0.04 is a second big blind in a different format.
self.re_PostBB = re.compile(r"^%(PLYR)s posts big blind %(CUR)s(?P<BB>[.0-9]+)" % subst, re.MULTILINE)
self.re_Antes = re.compile(r"^%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[.0-9]+)" % subst, re.MULTILINE)
self.re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst, re.MULTILINE)
self.re_PostBoth = re.compile(r"^%(PLYR)s: posts small \& big blinds %(CUR)s(?P<SBBB>[.0-9]+)" % subst, re.MULTILINE)
self.re_HeroCards = re.compile(r"^Dealing( \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\]) to %(PLYR)s" % subst, re.MULTILINE)
self.re_Action = re.compile(r"""
^%(PLYR)s(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds)(\sto)?
(\s(%(CUR)s)?(?P<BET>[.\d]+))?
""" % subst, re.MULTILINE|re.VERBOSE)
self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s (\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) )?(collected|showed \[.*\] and won) \(%(CUR)s(?P<POT>[.\d]+)\)(, mucked| with.*|)" % subst, re.MULTILINE)
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
def readSupportedGames(self):
return [["ring", "hold", "nl"],
["ring", "hold", "pl"],
["ring", "hold", "fl"],
["ring", "stud", "fl"],
["ring", "draw", "fl"],
["tour", "hold", "nl"],
["tour", "hold", "pl"],
["tour", "hold", "fl"],
["tour", "stud", "fl"],
]
def determineGameType(self, handText):
info = {}
m = self.re_GameInfo.search(handText)
if not m:
tmp = handText[0:100]
log.error(_("determineGameType: Unable to recognise gametype from: '%s'") % tmp)
log.error(_("determineGameType: Raising FpdbParseError"))
raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp)
mg = m.groupdict()
#print "DEBUG: %s" % mg
if 'LIMIT' in mg:
info['limitType'] = self.limits[mg['LIMIT']]
if 'GAME' in mg:
(info['base'], info['category']) = self.games[mg['GAME']]
if 'SB' in mg:
info['sb'] = mg['SB']
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = self.currencies[mg['CURRENCY']]
if 'TOURNO' in mg and mg['TOURNO'] is None:
info['type'] = 'ring'
else:
info['type'] = 'tour'
if info['limitType'] == 'fl' and info['bb'] is not None and info['type'] == 'ring' and info['base'] != 'stud':
try:
info['sb'] = self.Lim_Blinds[mg['BB']][0]
info['bb'] = self.Lim_Blinds[mg['BB']][1]
except KeyError:
log.error(_("determineGameType: Lim_Blinds has no lookup for '%s'" % mg['BB']))
log.error(_("determineGameType: Raising FpdbParseError"))
raise FpdbParseError(_("Lim_Blinds has no lookup for '%s'") % mg['BB'])
return info
def readHandInfo(self, hand):
info = {}
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if m:
info.update(m.groupdict())
# hand.maxseats = int(m2.group(1))
else:
pass # throw an exception here, eh?
m = self.re_GameInfo.search(hand.handText)
if m:
info.update(m.groupdict())
# m = self.re_Button.search(hand.handText)
# if m: info.update(m.groupdict())
# TODO : I rather like the idea of just having this dict as hand.info
log.debug("readHandInfo: %s" % info)
for key in info:
if key == 'DATETIME':
#2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]
#2008/08/17 - 01:14:43 (ET)
#2008/09/07 06:23:14 ET
m1 = self.re_DateTime.finditer(info[key])
datetimestr = "2000/01/01 00:00:00" # default used if time not found
for a in m1:
datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'), a.group('M'),a.group('D'),a.group('H'),a.group('MIN'),a.group('S'))
hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
if key == 'HID':
hand.handid = info[key]
if key == 'TOURNO':
hand.tourNo = info[key]
if key == 'BUYIN':
if info[key] == 'Freeroll':
hand.buyin = '$0+$0'
else:
#FIXME: The key looks like: '€0.82+€0.18 EUR'
# This should be parsed properly and used
hand.buyin = info[key]
if key == 'LEVEL':
hand.level = info[key]
if key == 'TABLE':
if hand.tourNo != None:
hand.tablename = re.split(" ", info[key])[1]
else:
hand.tablename = info[key]
if key == 'BUTTON':
hand.buttonpos = info[key]
if key == 'MAX':
hand.maxseats = int(info[key])
if key == 'MIXED':
hand.mixed = self.mixes[info[key]] if info[key] is not None else None
if key == 'PLAY' and info['PLAY'] is not None:
# hand.currency = 'play' # overrides previously set value
hand.gametype['currency'] = 'play'
def readButton(self, hand):
m = self.re_Button.search(hand.handText)
if m:
hand.buttonpos = int(m.group('BUTTON'))
else:
log.info('readButton: not found')
def readPlayerStacks(self, hand):
log.debug("readPlayerStacks")
m = self.re_PlayerInfo.finditer(hand.handText)
for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
if hand.gametype['base'] in ("hold"):
m = re.search(r"Dealing Cards(?P<PREFLOP>.+(?=Dealing Flop)|.+)"
r"(Dealing Flop(?P<FLOP> \[\S\S \S\S \S\S\].+(?=Dealing Turn)|.+))?"
r"(Dealing Turn (?P<TURN>\[\S\S\].+(?=Dealing River)|.+))?"
r"(Dealing River (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
elif hand.gametype['base'] in ("stud"):
m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3rd STREET \*\*\*)|.+)"
r"(\*\*\* 3rd STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4th STREET \*\*\*)|.+))?"
r"(\*\*\* 4th STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5th STREET \*\*\*)|.+))?"
r"(\*\*\* 5th STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 6th STREET \*\*\*)|.+))?"
r"(\*\*\* 6th STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* RIVER \*\*\*)|.+))?"
r"(\*\*\* RIVER \*\*\*(?P<SEVENTH>.+))?", hand.handText,re.DOTALL)
elif hand.gametype['base'] in ("draw"):
m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)"
r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* FIRST DRAW \*\*\*)|.+))?"
r"(\*\*\* FIRST DRAW \*\*\*(?P<DRAWONE>.+(?=\*\*\* SECOND DRAW \*\*\*)|.+))?"
r"(\*\*\* SECOND DRAW \*\*\*(?P<DRAWTWO>.+(?=\*\*\* THIRD DRAW \*\*\*)|.+))?"
r"(\*\*\* THIRD DRAW \*\*\*(?P<DRAWTHREE>.+))?", hand.handText,re.DOTALL)
hand.addStreets(m)
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
#print "DEBUG readCommunityCards:", street, hand.streets.group(street)
m = self.re_Board.search(hand.streets[street])
hand.setCommunityCards(street, m.group('CARDS').split(' '))
def readAntes(self, hand):
log.debug("reading antes")
m = self.re_Antes.finditer(hand.handText)
for player in m:
#~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
m = self.re_BringIn.search(hand.handText,re.DOTALL)
if m:
#~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
def readBlinds(self, hand):
try:
m = self.re_PostSB.search(hand.handText)
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind
hand.addBlind(None, None, None)
for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.re_PostBoth.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'both', a.group('SBBB'))
def readHeroCards(self, hand):
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
# we need to grab hero's cards
for street in ('PREFLOP', 'DEAL'):
if street in hand.streets.keys():
m = self.re_HeroCards.finditer(hand.streets[street])
for found in m:
# if m == None:
# hand.involved = False
# else:
hand.hero = found.group('PNAME')
newcards = found.group('NEWCARDS').split(' ')
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
for street, text in hand.streets.iteritems():
if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
m = self.re_HeroCards.finditer(hand.streets[street])
for found in m:
player = found.group('PNAME')
if found.group('NEWCARDS') is None:
newcards = []
else:
newcards = found.group('NEWCARDS').split(' ')
if found.group('OLDCARDS') is None:
oldcards = []
else:
oldcards = found.group('OLDCARDS').split(' ')
if street == 'THIRD' and len(newcards) == 3: # hero in stud game
hand.hero = player
hand.dealt.add(player) # need this for stud??
hand.addHoleCards(street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False)
else:
hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False)
def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street])
for action in m:
acts = action.groupdict()
if action.group('ATYPE') == ' raises':
hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' calls':
hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' bets':
hand.addBet( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' folds':
hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == ' discards':
hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('DISCARDED'))
elif action.group('ATYPE') == ' stands pat':
hand.addStandsPat( street, action.group('PNAME'))
else:
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
def readShowdownActions(self, hand):
# TODO: pick up mucks also??
for shows in self.re_ShowdownAction.finditer(hand.handText):
cards = shows.group('CARDS').split(' ')
hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand):
for m in self.re_CollectPot.finditer(hand.handText):
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
def readShownCards(self,hand):
for m in self.re_ShownCards.finditer(hand.handText):
if m.group('CARDS') is not None:
cards = m.group('CARDS')
cards = cards.split(' ') # needs to be a list, not a set--stud needs the order
(shown, mucked) = (False, False)
if m.group('SHOWED') == "showed": shown = True
elif m.group('SHOWED') == "mucked": mucked = True
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked)
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/stars/horse/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt")
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
#parser.add_option("-q", "--quiet", action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
#parser.add_option("-v", "--verbose", action="store_const", const=logging.INFO, dest="verbosity")
#parser.add_option("--vv", action="store_const", const=logging.DEBUG, dest="verbosity")
(options, args) = parser.parse_args()
e = PokerStars(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -225,13 +225,14 @@ class PokerStars(HandHistoryConverter):
def readHandInfo(self, hand): def readHandInfo(self, hand):
info = {} info = {}
m = self.re_HandInfo.search(hand.handText,re.DOTALL) m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if m: m2 = self.re_GameInfo.search(hand.handText)
info.update(m.groupdict()) if m is None or m2 is None:
else: logging.info("Didn't match re_HandInfo")
pass # throw an exception here, eh? logging.info(hand.handText)
m = self.re_GameInfo.search(hand.handText) raise FpdbParseError("No match in readHandInfo.")
if m:
info.update(m.groupdict()) info.update(m.groupdict())
info.update(m2.groupdict())
log.debug("readHandInfo: %s" % info) log.debug("readHandInfo: %s" % info)
for key in info: for key in info:

View File

@ -3968,6 +3968,7 @@ class Sql:
AND fee=%s AND fee=%s
AND category=%s AND category=%s
AND limitType=%s AND limitType=%s
AND maxSeats=%s
AND knockout=%s AND knockout=%s
AND rebuy=%s AND rebuy=%s
AND addOn=%s AND addOn=%s
@ -3977,9 +3978,9 @@ class Sql:
""" """
self.query['insertTourneyType'] = """INSERT INTO TourneyTypes self.query['insertTourneyType'] = """INSERT INTO TourneyTypes
(siteId, currency, buyin, fee, category, limitType, buyInChips, knockout, koBounty, rebuy, (siteId, currency, buyin, fee, category, limitType, maxSeats, buyInChips, knockout, koBounty, rebuy,
addOn ,speed, shootout, matrix, added, addedCurrency) addOn ,speed, shootout, matrix, added, addedCurrency)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""" """
self.query['getTourneyByTourneyNo'] = """SELECT t.* self.query['getTourneyByTourneyNo'] = """SELECT t.*

View File

@ -1,449 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Discover_Tables.py
Inspects the currently open windows and finds those of interest to us--that is
poker table windows from supported sites. Returns a list
of Table_Window objects representing the windows found.
"""
# Copyright 2008-2010, Ray E. Barker
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
# Standard Library modules
import os
import sys
import re
import locale
lang=locale.getdefaultlocale()[0][0:2]
if lang=="en":
def _(string): return string
else:
import gettext
try:
trans = gettext.translation("fpdb", localedir="locale", languages=[lang])
trans.install()
except IOError:
def _(string): return string
# Win32 modules
if os.name == 'nt':
import win32gui
import win32process
import win32api
import win32con
import win32security
# FreePokerTools modules
import Configuration
# Each TableWindow object must have the following attributes correctly populated:
# tw.name = the table name from the title bar, which must to match the table name
# from the corresponding hand history.
# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site
# name specified in the config file.
# tw.number = This is the system id number for the client table window in the
# format that the system presents it. This is Xid in Xwindows and
# hwnd in Microsoft Windows.
# tw.title = The full title from the window title bar.
# tw.width, tw.height = The width and height of the window in pixels. This is
# the internal width and height, not including the title bar and
# window borders.
# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative
# to the top left of the display screen. This also does not include the
# title bar and window borders. To put it another way, this is the
# screen location of (0, 0) in the working window.
class Table_Window:
def __init__(self, info = {}):
if 'number' in info: self.number = info['number']
if 'exe' in info: self.exe = info['exe']
if 'width' in info: self.width = info['width']
if 'height' in info: self.height = info['height']
if 'x' in info: self.x = info['x']
if 'y' in info: self.y = info['y']
if 'site' in info: self.site = info['site']
if 'title' in info: self.title = info['title']
if 'name' in info: self.name = info['name']
self.gdkhandle = None
def __str__(self):
# __str__ method for testing
temp = 'TableWindow object\n'
temp = temp + " name = %s\n site = %s\n number = %s\n title = %s\n" % (self.name, self.site, self.number, self.title)
# temp = temp + " game = %s\n structure = %s\n max = %s\n" % (self.game, self.structure, self.max)
temp = temp + " width = %d\n height = %d\n x = %d\n y = %d\n" % (self.width, self.height, self.x, self.y)
if getattr(self, 'tournament', 0):
temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table)
return temp
############################################################################
# Top-level discovery routines--these are the modules interface
def discover(c):
"""Dispatch routine for finding all potential poker client windows."""
if os.name == 'posix':
tables = discover_posix(c)
elif os.name == 'nt':
tables = discover_nt(c)
elif os.name == 'mac':
tables = discover_mac(c)
else:
tables = {}
return tables
def discover_table_by_name(c, tablename):
"""Dispatch routine for finding poker client windows with the given name."""
if os.name == 'posix':
info = discover_posix_by_name(c, tablename)
elif os.name == 'nt':
info = discover_nt_by_name(c, tablename)
elif os.name == 'mac':
info = discover_mac_by_name(c, tablename)
else:
return None
if info is None:
return None
return Table_Window(info)
def discover_tournament_table(c, tour_number, tab_number):
"""Dispatch routine for finding poker clients with tour and table number."""
if os.name == 'posix':
info = discover_posix_tournament(c, tour_number, tab_number)
elif os.name == 'nt':
info = discover_nt_tournament(c, tour_number, tab_number)
elif os.name == 'mac':
info = discover_mac_tournament(c, tour_number, tab_number)
else:
return None
if info:
return Table_Window(info)
return None
#############################################################################
# Posix (= XWindows) specific routines
def discover_posix(c):
"""Poker client table window finder for posix/Linux = XWindows."""
tables = {}
for listing in os.popen('xwininfo -root -tree').readlines():
# xwininfo -root -tree -id 0xnnnnn gets the info on a single window
for s in c.get_supported_sites():
params = c.get_site_parameters(s)
# TODO: We need to make a list of phrases, shared between the WIndows and Unix code!!!!!!
if re.search(params['table_finder'], listing):
if 'Lobby' in listing: continue
if 'Instant Hand History' in listing: continue
# if '\"Full Tilt Poker\"' in listing: continue
if 'History for table:' in listing: continue
if 'has no name' in listing: continue
info = decode_xwininfo(c, listing)
if info['site'] is None: continue
if info['title'] == info['exe']: continue
# this appears to be a poker client, so make a table object for it
tw = Table_Window(info)
eval("%s(tw)" % params['decoder'])
tables[tw.name] = tw
return tables
def discover_posix_by_name(c, tablename):
"""Find an XWindows poker client of the given name."""
for listing in os.popen('xwininfo -root -tree').readlines():
if tablename in listing:
if 'History for table:' in listing: continue
info = decode_xwininfo(c, listing)
if not info['name'] == tablename: continue
return info
return None
def discover_posix_tournament(c, t_number, s_number):
"""Finds the X window for a client, given tournament and table nos."""
search_string = "%s.+Table.+%s" % (t_number, s_number)
for listing in os.popen('xwininfo -root -tree').readlines():
if re.search(search_string, listing):
return decode_xwininfo(c, listing)
return None
def decode_xwininfo(c, info_string):
"""Gets window parameters from xwinifo string--XWindows."""
info = {}
mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', info_string)
if not mo:
return None
else:
info['number'] = int( mo.group(1), 0)
info['exe'] = mo.group(3)
info['width'] = int( mo.group(4) )
info['height'] = int( mo.group(5) )
info['x'] = int( mo.group(6) )
info['y'] = int( mo.group(7) )
info['site'] = get_site_from_exe(c, info['exe'])
info['title'] = re.sub('\"', '', mo.group(2))
title_bits = re.split(' - ', info['title'])
info['name'] = clean_title(title_bits[0])
return info
##############################################################################
# NT (= Windows) specific routines
def discover_nt(c):
""" Poker client table window finder for Windows."""
#
# I cannot figure out how to get the inside dimensions of the poker table
# windows. So I just assume all borders are 3 thick and all title bars
# are 29 high. No doubt this will be off when used with certain themes.
#
b_width = 3
tb_height = 29
titles = {}
tables = {}
win32gui.EnumWindows(win_enum_handler, titles)
for hwnd in titles:
if 'Logged In as' in titles[hwnd] and not 'Lobby' in titles[hwnd]:
if 'Full Tilt Poker' in titles[hwnd]:
continue
tw = Table_Window()
tw.number = hwnd
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
tw.title = titles[hwnd]
tw.width = int( width ) - 2*b_width
tw.height = int( height ) - b_width - tb_height
tw.x = int( x ) + b_width
tw.y = int( y ) + tb_height
# TODO: Isn't the site being determined by the EXE name it belongs to? is this section of code even useful? cleaning it anyway
if 'Logged In as' in titles[hwnd]:
tw.site = "PokerStars"
elif 'Logged In As' in titles[hwnd]:
tw.site = "Full Tilt"
else:
tw.site = "Unknown"
sys.stderr.write(_("Found unknown table = %s") % tw.title)
if tw.site != "Unknown":
eval("%s(tw)" % c.supported_sites[tw.site].decoder)
else:
tw.name = "Unknown"
tables[len(tables)] = tw
return tables
def discover_nt_by_name(c, tablename):
"""Finds poker client window with the given table name."""
titles = {}
win32gui.EnumWindows(win_enum_handler, titles)
for hwnd in titles:
#print "Tables.py: tablename =", tablename, "title =", titles[hwnd]
try:
# maybe it's better to make global titles[hwnd] decoding?
# this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html
if not tablename.lower() in titles[hwnd].decode(Configuration.LOCALE_ENCODING).lower():
continue
except:
continue
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window
if 'HUD:' in titles[hwnd]: continue # FPDB HUD window
if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows
if ' - Table ' in titles[hwnd]: continue # Absolute table Chat window.. sigh. TODO: Can we tell what site we're trying to discover for somehow in here, so i can limit this check just to AP searches?
temp = decode_windows(c, titles[hwnd], hwnd)
print _("attach to window"), temp
return temp
return None
def discover_nt_tournament(c, tour_number, tab_number):
"""Finds the Windows window handle for the given tournament/table."""
search_string = "%s.+%s" % (tour_number, tab_number)
titles ={}
win32gui.EnumWindows(win_enum_handler, titles)
for hwnd in titles:
# Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows
if 'Chat:' in titles[hwnd]: continue
# Everleaf Network HH viewer window
if 'History for table:' in titles[hwnd]: continue
# FPDB HUD window
if 'HUD:' in titles[hwnd]: continue
if re.search(search_string, titles[hwnd]):
return decode_windows(c, titles[hwnd], hwnd)
return None
def get_nt_exe(hwnd):
"""Finds the name of the executable that the given window handle belongs to."""
# Request privileges to enable "debug process", so we can later use
# PROCESS_VM_READ, retardedly required to GetModuleFileNameEx()
priv_flags = win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY
hToken = win32security.OpenProcessToken (win32api.GetCurrentProcess(),
priv_flags)
# enable "debug process"
privilege_id = win32security.LookupPrivilegeValue(None,
win32security.SE_DEBUG_NAME)
old_privs = win32security.AdjustTokenPrivileges(hToken, 0,
[(privilege_id,
win32security.SE_PRIVILEGE_ENABLED)])
# Open the process, and query it's filename
processid = win32process.GetWindowThreadProcessId(hwnd)
pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION |
win32con.PROCESS_VM_READ, False,
processid[1])
try:
exename = win32process.GetModuleFileNameEx(pshandle, 0)
except pywintypes.error:
# insert code to call GetProcessImageName if we can find it..
# returning None from here will hopefully break all following code
exename = None
finally:
# clean up
win32api.CloseHandle(pshandle)
win32api.CloseHandle(hToken)
return exename
def decode_windows(c, title, hwnd):
"""Gets window parameters from the window title and handle--Windows."""
# I cannot figure out how to get the inside dimensions of the poker table
# windows. So I just assume all borders are 3 thick and all title bars
# are 29 high. No doubt this will be off when used with certain themes.
b_width = 3
tb_height = 29
info = {}
info['number'] = hwnd
info['title'] = re.sub('\"', '', title)
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
info['x'] = int(x) + b_width
info['y'] = int( y ) + tb_height
info['width'] = int( width ) - 2*b_width
info['height'] = int( height ) - b_width - tb_height
info['exe'] = get_nt_exe(hwnd)
print "get_nt_exe returned ", info['exe']
# TODO: 'width' here is all sorts of screwed up.
title_bits = re.split(' - ', info['title'])
info['name'] = title_bits[0]
info['site'] = get_site_from_exe(c, info['exe'])
return info
def win_enum_handler(hwnd, titles):
str = win32gui.GetWindowText(hwnd)
if str != "":
titles[hwnd] = win32gui.GetWindowText(hwnd)
###################################################################
# Utility routines used by all the discoverers.
def get_site_from_exe(c, exe):
"""Look up the site from config, given the exe."""
for s in c.get_supported_sites():
params = c.get_site_parameters(s)
if re.search(params['table_finder'], exe):
return params['site_name']
return None
def everleaf_decode_table(tw):
# 2 - Tournament ID: 573256 - NL Hold'em - 150/300 blinds - Good luck <username>! - [Connection is ...]
pass
def pokerstars_decode_table(tw):
# Extract poker information from the window title. This is not needed for
# fpdb, since all that information is available in the db via new_hand_number.
# This is needed only when using the HUD with a backend less integrated.
title_bits = re.split(' - ', tw.title)
name = title_bits[0]
mo = re.search('Tournament (\d+) Table (\d+)', name)
if mo:
tw.tournament = int( mo.group(1) )
tw.table = int( mo.group(2) )
tw.name = name
else:
tw.tournament = None
tw.name = clean_title(name)
mo = re.search('(Razz|Stud H/L|Stud|Omaha H/L|Omaha|Hold\'em|5-Card Draw|Triple Draw 2-7 Lowball|Badugi)', tw.title)
tw.game = mo.group(1).lower()
tw.game = re.sub('\'', '', tw.game)
tw.game = re.sub('h/l', 'hi/lo', tw.game)
mo = re.search('(No Limit|Pot Limit)', tw.title)
if mo:
tw.structure = mo.group(1).lower()
else:
tw.structure = 'limit'
tw.max = None
if tw.game in ('razz', 'stud', 'stud hi/lo'):
tw.max = 8
elif tw.game in ('5-card draw', 'triple draw 2-7 lowball'):
tw.max = 6
elif tw.game == 'holdem':
pass
elif tw.game in ('omaha', 'omaha hi/lo'):
pass
def fulltilt_decode_table(tw):
# Extract poker information from the window title. This is not needed for
# fpdb, since all that information is available in the db via new_hand_number.
# This is needed only when using the HUD with a backend less integrated.
title_bits = re.split(' - ', tw.title)
name = title_bits[0]
tw.tournament = None
tw.name = clean_title(name)
def clean_title(name):
"""Clean the little info strings from the table name."""
# these strings could go in a config file
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
' \(deep hu\)', ' \(deep 6\)', '\(6 max, deep\)', ' \(2\)',
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
' \(speed\)', 'special', 'newVPP',
' no all-in', ' fast', ',', ' 50BB min', '50bb min', '\s+$']:
name = re.sub(pattern, '', name)
name = name.rstrip()
return name
###########################################################################
# Mac specific routines....all stubs for now
def discover_mac_tournament(c, tour_number, tab_number):
"""Mac users need help."""
return None
def discover_mac(c):
"""Poker client table window finder for Macintosh."""
tables = {}
return tables
def discover_mac_by_name(c, tablename):
"""Oh, the humanity."""
# again, i have no mac to test this on, sorry -eric
return None
###########################################################################
# Main function used for testing
if __name__=="__main__":
c = Configuration.Config()
print discover_table_by_name(c, "Torino")
# print discover_tournament_table(c, "118942908", "3")
tables = discover(c)
for t in tables.keys():
print tables[t]
print _("press enter to continue")
sys.stdin.readline()

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import sys import sys
import os import os
import codecs import codecs
@ -9,13 +10,31 @@ import Database
import SQL import SQL
import fpdb_import import fpdb_import
def error_report( filename, hand, stat, ghash, testhash, player):
class FpdbError:
def __init__(self, sitename):
self.site = sitename
self.errorcount = 0
self.histogram = {}
def error_report(self, filename, hand, stat, ghash, testhash, player):
print "Regression Test Error:" print "Regression Test Error:"
print "\tFile: %s" % filename print "\tFile: %s" % filename
print "\tStat: %s" % stat print "\tStat: %s" % stat
print "\tPlayer: %s" % player print "\tPlayer: %s" % player
if filename in self.histogram:
self.histogram[filename] += 1
else:
self.histogram[filename] = 1
self.errorcount += 1
def compare(leaf, importer): def print_histogram(self):
print "%s:" % self.site
for f in self.histogram:
idx = f.find('regression')
print "(%3d) : %s" %(self.histogram[f], f[idx:])
def compare(leaf, importer, errors):
filename = leaf filename = leaf
#print "DEBUG: fileanme: %s" % filename #print "DEBUG: fileanme: %s" % filename
@ -51,21 +70,21 @@ def compare(leaf, importer):
pass pass
else: else:
# Stats don't match - Doh! # Stats don't match - Doh!
error_report(filename, hand, stat, ghash, testhash, p) errors.error_report(filename, hand, stat, ghash, testhash, p)
importer.clearFileList() importer.clearFileList()
def walk_testfiles(dir, function, importer): def walk_testfiles(dir, function, importer, errors):
"""Walks a directory, and executes a callback on each file """ """Walks a directory, and executes a callback on each file """
dir = os.path.abspath(dir) dir = os.path.abspath(dir)
for file in [file for file in os.listdir(dir) if not file in [".",".."]]: for file in [file for file in os.listdir(dir) if not file in [".",".."]]:
nfile = os.path.join(dir,file) nfile = os.path.join(dir,file)
if os.path.isdir(nfile): if os.path.isdir(nfile):
walk_testfiles(nfile, compare, importer) walk_testfiles(nfile, compare, importer, errors)
else: else:
compare(nfile, importer) compare(nfile, importer, errors)
def main(argv=None): def main(argv=None):
if argv is None: if argv is None:
@ -76,7 +95,6 @@ def main(argv=None):
sql = SQL.Sql(db_server = 'sqlite') sql = SQL.Sql(db_server = 'sqlite')
settings = {} settings = {}
settings.update(config.get_db_parameters()) settings.update(config.get_db_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())
db.recreate_tables() db.recreate_tables()
@ -87,7 +105,25 @@ def main(argv=None):
importer.setCallHud(False) importer.setCallHud(False)
importer.setFakeCacheHHC(True) importer.setFakeCacheHHC(True)
walk_testfiles("regression-test-files/cash/Stars/", compare, importer) PokerStarsErrors = FpdbError('PokerStars')
FTPErrors = FpdbError('Full Tilt Poker')
PartyPokerErrors = FpdbError('Party Poker')
BetfairErrors = FpdbError('Betfair')
walk_testfiles("regression-test-files/cash/Stars/", compare, importer, PokerStarsErrors)
walk_testfiles("regression-test-files/cash/FTP/", compare, importer, FTPErrors)
walk_testfiles("regression-test-files/cash/PartyPoker/", compare, importer, PartyPokerErrors)
walk_testfiles("regression-test-files/cash/Betfair/", compare, importer, BetfairErrors)
totalerrors = PokerStarsErrors.errorcount + FTPErrors.errorcount + PartyPokerErrors.errorcount + BetfairErrors.errorcount
print "---------------------"
print "Total Errors: %d" % totalerrors
print "---------------------"
PokerStarsErrors.print_histogram()
FTPErrors.print_histogram()
PartyPokerErrors.print_histogram()
BetfairErrors.print_histogram()
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())

View File

@ -77,7 +77,7 @@ class TourneySummary(object):
self.buyin = None self.buyin = None
self.fee = None self.fee = None
self.hero = None self.hero = None
self.maxseats = None self.maxseats = 0
self.entries = 0 self.entries = 0
self.speed = "Normal" self.speed = "Normal"
self.prizepool = 0 # Make it a dict in order to deal (eventually later) with non-money winnings : {'MONEY' : amount, 'OTHER' : Value ??} self.prizepool = 0 # Make it a dict in order to deal (eventually later) with non-money winnings : {'MONEY' : amount, 'OTHER' : Value ??}

View File

@ -40,6 +40,18 @@ import win32api
import win32con import win32con
import win32security import win32security
import locale
lang=locale.getdefaultlocale()[0][0:2]
if lang=="en":
def _(string): return string
else:
import gettext
try:
trans = gettext.translation("fpdb", localedir="locale", languages=[lang])
trans.install()
except IOError:
def _(string): return string
# FreePokerTools modules # FreePokerTools modules
from TableWindow import Table_Window from TableWindow import Table_Window

View File

@ -1 +0,0 @@
python /usr/share/doc/python-2.7/examples/Tools/i18n/msgfmt.py --output-file=locale/hu/LC_MESSAGES/fpdb.mo locale/fpdb-hu_HU.po

View File

@ -129,7 +129,7 @@ import Configuration
import Exceptions import Exceptions
import Stats import Stats
VERSION = "0.20.904 plus git" VERSION = "0.20.906"
class fpdb: class fpdb:
@ -264,7 +264,8 @@ class fpdb:
, ('numpy', numpy_version) , ('numpy', numpy_version)
, ('sqlite3', sqlite3_version) , ('sqlite3', sqlite3_version)
, ('sqlite', sqlite_version) , ('sqlite', sqlite_version)
, ('database', self.settings['db-server'] + db_version) , ('fpdb version', VERSION)
, ('database used', self.settings['db-server'])
] ]
versions = gtk.TextBuffer() versions = gtk.TextBuffer()
w = 20 # width used for module names and version numbers w = 20 # width used for module names and version numbers
@ -786,7 +787,6 @@ class fpdb:
<menuitem action="Quit"/> <menuitem action="Quit"/>
</menu> </menu>
<menu action="import"> <menu action="import">
<menuitem action="sethharchive"/>
<menuitem action="bulkimp"/> <menuitem action="bulkimp"/>
<menuitem action="imapimport"/> <menuitem action="imapimport"/>
<menuitem action="autoimp"/> <menuitem action="autoimp"/>
@ -828,15 +828,14 @@ class fpdb:
('SaveProf', None, _('_Save Profile (todo)'), _('<control>S'), 'Save your profile', self.dia_save_profile), ('SaveProf', None, _('_Save Profile (todo)'), _('<control>S'), 'Save your profile', self.dia_save_profile),
('Preferences', None, _('Pre_ferences'), _('<control>F'), 'Edit your preferences', self.dia_preferences), ('Preferences', None, _('Pre_ferences'), _('<control>F'), 'Edit your preferences', self.dia_preferences),
('import', None, _('_Import')), ('import', None, _('_Import')),
('sethharchive', None, _('_Set HandHistory Archive Directory'), None, 'Set HandHistory Archive Directory', self.select_hhArchiveBase),
('bulkimp', None, _('_Bulk Import'), _('<control>B'), 'Bulk Import', self.tab_bulk_import), ('bulkimp', None, _('_Bulk Import'), _('<control>B'), 'Bulk Import', self.tab_bulk_import),
('imapimport', None, _('_Import through eMail/IMAP'), _('<control>I'), 'Import through eMail/IMAP', self.tab_imap_import), ('imapimport', None, _('_Import through eMail/IMAP'), _('<control>I'), 'Import through eMail/IMAP', self.tab_imap_import),
('viewers', None, _('_Viewers')), ('viewers', None, _('_Viewers')),
('autoimp', None, _('_Auto Import and HUD'), _('<control>A'), 'Auto Import and HUD', self.tab_auto_import), ('autoimp', None, _('_Auto Import and HUD'), _('<control>A'), 'Auto Import and HUD', self.tab_auto_import),
('hudConfigurator', None, _('_HUD Configurator'), _('<control>H'), 'HUD Configurator', self.diaHudConfigurator), ('hudConfigurator', None, _('_HUD Configurator'), _('<control>H'), 'HUD Configurator', self.diaHudConfigurator),
('graphs', None, _('_Graphs'), _('<control>G'), 'Graphs', self.tabGraphViewer), ('graphs', None, _('_Graphs'), _('<control>G'), 'Graphs', self.tabGraphViewer),
('ringplayerstats', None, _('Ring _Player Stats (tabulated view)'), _('<control>P'), 'Ring Player Stats (tabulated view)', self.tab_ring_player_stats), ('ringplayerstats', None, _('Ring _Player Stats (tabulated view, not on pgsql)'), _('<control>P'), 'Ring Player Stats (tabulated view)', self.tab_ring_player_stats),
('tourneyplayerstats', None, _('_Tourney Player Stats (tabulated view)'), _('<control>T'), 'Tourney Player Stats (tabulated view, mysql only)', self.tab_tourney_player_stats), ('tourneyplayerstats', None, _('_Tourney Player Stats (tabulated view, not on pgsql)'), _('<control>T'), 'Tourney Player Stats (tabulated view, mysql only)', self.tab_tourney_player_stats),
('tourneyviewer', None, _('Tourney _Viewer'), None, 'Tourney Viewer)', self.tab_tourney_viewer_stats), ('tourneyviewer', None, _('Tourney _Viewer'), None, 'Tourney Viewer)', self.tab_tourney_viewer_stats),
('posnstats', None, _('P_ositional Stats (tabulated view, not on sqlite)'), _('<control>O'), 'Positional Stats (tabulated view)', self.tab_positional_stats), ('posnstats', None, _('P_ositional Stats (tabulated view, not on sqlite)'), _('<control>O'), 'Positional Stats (tabulated view)', self.tab_positional_stats),
('sessionstats', None, _('Session Stats'), None, 'Session Stats', self.tab_session_stats), ('sessionstats', None, _('Session Stats'), None, 'Session Stats', self.tab_session_stats),
@ -888,7 +887,6 @@ class fpdb:
self.settings.update({'cl_options': cl_options}) self.settings.update({'cl_options': cl_options})
self.settings.update(self.config.get_db_parameters()) self.settings.update(self.config.get_db_parameters())
self.settings.update(self.config.get_tv_parameters())
self.settings.update(self.config.get_import_parameters()) self.settings.update(self.config.get_import_parameters())
self.settings.update(self.config.get_default_paths()) self.settings.update(self.config.get_default_paths())
@ -1253,16 +1251,6 @@ You can find the full license texts in agpl-3.0.txt, gpl-2.0.txt, gpl-3.0.txt an
elif response == gtk.RESPONSE_NO: elif response == gtk.RESPONSE_NO:
self.select_hhArchiveBase() self.select_hhArchiveBase()
def select_hhArchiveBase(self, widget=None):
fc = gtk.FileChooserDialog(title=_("Select HH Output Directory"), parent=None, action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, buttons=(gtk.STOCK_OPEN,gtk.RESPONSE_OK), backend=None)
fc.run()
# TODO: We need to put in a Cancel button, and handle if the user presses that or the "Close" box without selecting anything as a cancel, and return to the prior setting
#self.warning_box("You selected %s" % fc.get_filename())
self.config.set_hhArchiveBase(fc.get_filename())
self.config.save()
self.load_profile() # we can't do this at the end of this func because load_profile calls this func
fc.destroy() # TODO: loop this to make sure we get valid data back from it, because the open directory thing in GTK lets you select files and not select things and other stupid bullshit
def main(self): def main(self):
gtk.main() gtk.main()
return 0 return 0

View File

@ -10,6 +10,18 @@ import time
import signal import signal
import base64 import base64
import locale
lang=locale.getdefaultlocale()[0][0:2]
if lang=="en":
def _(string): return string
else:
import gettext
try:
trans = gettext.translation("fpdb", localedir="locale", languages=[lang])
trans.install()
except IOError:
def _(string): return string
InterProcessLock = None InterProcessLock = None
""" """

View File

@ -0,0 +1,4 @@
cd ..
python /usr/share/doc/python-2.7/examples/Tools/i18n/msgfmt.py --output-file=locale/hu/LC_MESSAGES/fpdb.mo locale/fpdb-hu_HU.po
python /usr/share/doc/python-2.7/examples/Tools/i18n/msgfmt.py --output-file=locale/de/LC_MESSAGES/fpdb.mo locale/fpdb-de_DE.po

View File

@ -1,2 +1,3 @@
cd ..
python /usr/share/doc/python-2.7/examples/Tools/i18n/pygettext.py --output-dir=locale --default-domain=fpdb --output=fpdb-en_GB.po *.py* python /usr/share/doc/python-2.7/examples/Tools/i18n/pygettext.py --output-dir=locale --default-domain=fpdb --output=fpdb-en_GB.po *.py*

Binary file not shown.

3102
pyfpdb/locale/fpdb-de_DE.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,2 +1,3 @@
msgmerge --update fpdb-hu_HU.po fpdb-en_GB.po msgmerge --update fpdb-hu_HU.po fpdb-en_GB.po
msgmerge --update fpdb-de_DE.po fpdb-en_GB.po

View File

@ -0,0 +1,33 @@
Game #9485557849 starts.
#Game No : 9485557849
***** Hand History for Game 9485557849 *****
$0.80 USD NL Texas Hold'em - Saturday, July 31, 13:52:16 EDT 2010
Table 20BB Min Speed #1770998 (Real Money)
Seat 1 is the button
Total number of players : 4/9
Seat 3: Player1 ( $1.64 USD )
Seat 5: Player2 ( $0.01 USD )
Seat 9: Player3 ( $1.02 USD )
Seat 1: Player4 ( $1.20 USD )
Player1 posts small blind [$0.01 USD].
Player2 posts big blind [$0.01 USD].
** Dealing down cards **
Dealt to Player1 [ 8h Kc ]
Player3 folds
Player4 calls [$0.02 USD]
Player1 calls [$0.01 USD]
** Dealing Flop ** [ Td, 7c, 9h ]
Player1 checks
Player4 checks
** Dealing Turn ** [ 3h ]
Player1 checks
Player4 checks
** Dealing River ** [ Jc ]
Player1 bets [$0.04 USD]
Player4 folds
Player1 shows [ 8h, Kc ]a straight, Seven to Jack.
Player2 doesn't show [ Ts, Jd ]two pairs, Jacks and Tens.
Player1 wins $0.06 USD from the side pot 1 with a straight, Seven to Jack.
Player1 wins $0.03 USD from the main pot with a straight, Seven to Jack.
Player2 has left the table.

View File

@ -1,63 +0,0 @@
Game #9485557849 starts.
#Game No : 9485557849
***** Hand History for Game 9485557849 *****
$0.80 USD NL Texas Hold'em - Saturday, July 31, 13:52:16 EDT 2010
Table 20BB Min Speed #1770998 (Real Money)
Seat 1 is the button
Total number of players : 4/9
Seat 3: FErki84 ( $1.64 USD )
Seat 5: Vandercasses ( $0.01 USD )
Seat 9: jeremyho888 ( $1.02 USD )
Seat 1: sergeodem ( $1.20 USD )
FErki84 posts small blind [$0.01 USD].
Vandercasses posts big blind [$0.01 USD].
** Dealing down cards **
Dealt to FErki84 [ 8h Kc ]
jeremyho888 folds
sergeodem calls [$0.02 USD]
FErki84 calls [$0.01 USD]
** Dealing Flop ** [ Td, 7c, 9h ]
FErki84 checks
sergeodem checks
** Dealing Turn ** [ 3h ]
FErki84 checks
sergeodem checks
** Dealing River ** [ Jc ]
FErki84 bets [$0.04 USD]
sergeodem folds
FErki84 shows [ 8h, Kc ]a straight, Seven to Jack.
Vandercasses doesn't show [ Ts, Jd ]two pairs, Jacks and Tens.
FErki84 wins $0.06 USD from the side pot 1 with a straight, Seven to Jack.
FErki84 wins $0.03 USD from the main pot with a straight, Seven to Jack.
Vandercasses has left the table.
Game #9498788316 starts.
#Game No : 9498788316
***** Hand History for Game 9498788316 *****
$1.60 USD NL Texas Hold'em - Wednesday, August 04, 15:02:33 EDT 2010
Table 20BB Min #1847547 (No DP) (Real Money)
Seat 2 is the button
Total number of players : 5/6
Seat 5: CepguTbIu999 ( $1.60 USD )
Seat 1: Daytona_955 ( $2.45 USD )
Seat 4: FErki84 ( $2.18 USD )
Seat 2: anjl2009 ( $2.80 USD )
Seat 3: lukeman2 ( $0.01 USD )
lukeman2 posts small blind [$0.01 USD].
FErki84 posts big blind [$0.04 USD].
** Dealing down cards **
Dealt to FErki84 [ 6s 2c ]
CepguTbIu999 folds
Daytona_955 folds
anjl2009 folds
** Dealing Flop ** [ 9d, Ah, 3h ]
** Dealing Turn ** [ Js ]
** Dealing River ** [ Kc ]
lukeman2 shows [ 5h, 5s ]a pair of Fives.
FErki84 shows [ 6s, 2c ]high card Ace.
FErki84 wins $0.03 USD from the side pot 1 with high card, Ace.
lukeman2 wins $0.02 USD from the main pot with a pair of Fives.
lukeman2 has left the table.

View File

@ -0,0 +1,28 @@
Game #9498788316 starts.
#Game No : 9498788316
***** Hand History for Game 9498788316 *****
$1.60 USD NL Texas Hold'em - Wednesday, August 04, 15:02:33 EDT 2010
Table 20BB Min #1847547 (No DP) (Real Money)
Seat 2 is the button
Total number of players : 5/6
Seat 5: Player1 ( $1.60 USD )
Seat 1: Player2 ( $2.45 USD )
Seat 4: Player3 ( $2.18 USD )
Seat 2: Player4 ( $2.80 USD )
Seat 3: Player5 ( $0.01 USD )
Player5 posts small blind [$0.01 USD].
Player3 posts big blind [$0.04 USD].
** Dealing down cards **
Dealt to Player3 [ 6s 2c ]
Player1 folds
Player2 folds
Player4 folds
** Dealing Flop ** [ 9d, Ah, 3h ]
** Dealing Turn ** [ Js ]
** Dealing River ** [ Kc ]
Player5 shows [ 5h, 5s ]a pair of Fives.
Player3 shows [ 6s, 2c ]high card Ace.
Player3 wins $0.03 USD from the side pot 1 with high card, Ace.
Player5 wins $0.02 USD from the main pot with a pair of Fives.
Player5 has left the table.

View File

@ -1,5 +1,5 @@
fpdb database dump fpdb database dump
DB version=142 DB version=143
################### ###################
Table Autorates Table Autorates
@ -1644,10 +1644,20 @@ Table Players
commentTs=None commentTs=None
###################
Table RawHands
###################
empty table
###################
Table RawTourneys
###################
empty table
################### ###################
Table Settings Table Settings
################### ###################
version=142 version=143
################### ###################

View File

@ -0,0 +1,752 @@
{ u'123smoothie': { 'card1': 0,
'card2': 0,
'card3': 29,
'card4': 28,
'card5': 47,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': True,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': True,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 1,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 2,
'sitout': False,
'startCards': 0,
'startCash': 99,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 1,
'street0Raises': 0,
'street0VPI': True,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': True,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': True,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': True,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -3,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'Soroka69': { 'card1': 0,
'card2': 0,
'card3': 19,
'card4': 43,
'card5': 30,
'card6': 40,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': True,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': True,
'otherRaisedStreet3': True,
'otherRaisedStreet4': True,
'position': 4,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 7,
'sitout': False,
'startCards': 0,
'startCash': 83,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 1,
'street0Raises': 0,
'street0VPI': True,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': True,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 1,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': True,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 1,
'street3CheckCallRaiseChance': True,
'street3CheckCallRaiseDone': True,
'street3Raises': 0,
'street3Seen': True,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': True,
'street4CheckCallRaiseDone': True,
'street4Raises': 0,
'street4Seen': True,
'totalProfit': -19,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'TomSludge': { 'card1': 0,
'card2': 0,
'card3': 46,
'card4': 0,
'card5': 0,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 5,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 6,
'sitout': False,
'startCards': 0,
'startCash': 158,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 0,
'street0Raises': 0,
'street0VPI': False,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': False,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': False,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -1,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'denny501': { 'card1': 0,
'card2': 0,
'card3': 27,
'card4': 45,
'card5': 0,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 'S',
'raiseFirstInChance': True,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 4,
'sitout': False,
'startCards': 0,
'startCash': 71,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 0,
'street0Raises': 0,
'street0VPI': False,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': True,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': False,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -3,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'gashpor': { 'card1': 3,
'card2': 15,
'card3': 17,
'card4': 24,
'card5': 23,
'card6': 21,
'card7': 5,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 0,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 2,
'sawShowdown': True,
'seatNo': 3,
'sitout': False,
'startCards': 0,
'startCash': 140,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 1,
'street0Raises': 0,
'street0VPI': True,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': True,
'street2Aggr': True,
'street2Bets': 1,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': True,
'street3Aggr': True,
'street3Bets': 1,
'street3CBChance': True,
'street3CBDone': True,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': True,
'street4Aggr': True,
'street4Bets': 1,
'street4CBChance': True,
'street4CBDone': True,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': True,
'totalProfit': 13,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 40,
'wonAtSD': 1.0,
'wonWhenSeenStreet1': 1.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'rdiezchang': { 'card1': 0,
'card2': 0,
'card3': 26,
'card4': 49,
'card5': 48,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': True,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': True,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 3,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 8,
'sitout': False,
'startCards': 0,
'startCash': 205,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 1,
'street0Raises': 0,
'street0VPI': True,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': True,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': True,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': True,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -3,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u's0rrow': { 'card1': 32,
'card2': 41,
'card3': 4,
'card4': 37,
'card5': 38,
'card6': 18,
'card7': 16,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': True,
'otherRaisedStreet3': True,
'otherRaisedStreet4': True,
'position': 6,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 2,
'sawShowdown': True,
'seatNo': 5,
'sitout': False,
'startCards': 0,
'startCash': 152,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 1,
'street0Raises': 0,
'street0VPI': True,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': True,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 1,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': True,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 1,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': True,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 1,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': True,
'totalProfit': 13,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 40,
'wonAtSD': 1.0,
'wonWhenSeenStreet1': 1.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'u.pressure': { 'card1': 0,
'card2': 0,
'card3': 22,
'card4': 0,
'card5': 0,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 2,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 1,
'sitout': False,
'startCards': 0,
'startCash': 1117,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 0,
'street0Raises': 0,
'street0VPI': False,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': False,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': False,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -1,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0}}

View File

@ -1,5 +1,5 @@
fpdb database dump fpdb database dump
DB version=142 DB version=143
################### ###################
Table Autorates Table Autorates
@ -41,10 +41,20 @@ Table Players
################### ###################
empty table empty table
###################
Table RawHands
###################
empty table
###################
Table RawTourneys
###################
empty table
################### ###################
Table Settings Table Settings
################### ###################
version=142 version=143
################### ###################

View File

@ -1,5 +1,5 @@
fpdb database dump fpdb database dump
DB version=142 DB version=143
################### ###################
Table Autorates Table Autorates
@ -1857,7 +1857,6 @@ Table HandsPlayers
street4Raises=0 street4Raises=0
actionString=None actionString=None
!!!verified to here
################### ###################
Table HudCache Table HudCache
################### ###################
@ -2557,6 +2556,7 @@ Table HudCache
street3Raises=0 street3Raises=0
street4Raises=0 street4Raises=0
!!!verified to here
id=9 id=9
gametypeId=2 gametypeId=2
playerId=1 playerId=1
@ -3441,10 +3441,20 @@ Table Players
commentTs=None commentTs=None
###################
Table RawHands
###################
empty table
###################
Table RawTourneys
###################
empty table
################### ###################
Table Settings Table Settings
################### ###################
version=142 version=143
################### ###################

View File

@ -30,7 +30,6 @@ sql = SQL.Sql(db_server = 'sqlite')
settings = {} settings = {}
settings.update(config.get_db_parameters()) settings.update(config.get_db_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

@ -30,7 +30,6 @@ sql = SQL.Sql(db_server = 'sqlite')
settings = {} settings = {}
settings.update(config.get_db_parameters()) settings.update(config.get_db_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

@ -10,6 +10,36 @@
# Python packaging for fpdb # Python packaging for fpdb
from distutils.core import setup from distutils.core import setup
from distutils.command.install_data import install_data as INST
import glob, string, os
class inst_translations(INST):
# Return triples for installations
def __locales(self, rootdir):
_globstr = '%s/*/*/*.mo' % rootdir
paths = glob.glob(_globstr)
_locales = []
for p in paths:
rp = string.split(p, '/', 2)
(lang, loc, mo) = string.split(rp[2], '/')
_locales.append( (lang, loc, mo) )
return _locales
def run(self):
locales = self.__locales('pyfpdb/locale')
for (lang, loc, mo_file) in locales:
lang_dir = os.path.join('share', 'locale', lang, loc)
lang_file = os.path.join('pyfpdb/locale', lang, loc, mo_file)
self.data_files.append( (lang_dir, [lang_file]) )
INST.run(self)
commands = {
'install_data': inst_translations
}
setup(name = 'fpdb', setup(name = 'fpdb',
description = 'Free Poker Database', description = 'Free Poker Database',
@ -18,6 +48,7 @@ setup(name = 'fpdb',
author_email = 'fpdb-main@lists.sourceforge.net', author_email = 'fpdb-main@lists.sourceforge.net',
packages = ['fpdb'], packages = ['fpdb'],
package_dir = { 'fpdb' : 'pyfpdb' }, package_dir = { 'fpdb' : 'pyfpdb' },
cmdclass = commands,
data_files = [ data_files = [
('/usr/share/pixmaps', ('/usr/share/pixmaps',
['gfx/fpdb-icon.png', 'gfx/fpdb-icon2.png', ['gfx/fpdb-icon.png', 'gfx/fpdb-icon2.png',