diff --git a/docs/readme-dev.txt b/docs/readme-dev.txt
index 25e013bd..c8b5cd3f 100644
--- a/docs/readme-dev.txt
+++ b/docs/readme-dev.txt
@@ -1,67 +1,67 @@
-Hi,
-This document is to serve as a little overview (later: full technical doc) for current and prospective developers with:
-a) introduction into the code structure
-b) organisational/legal things
-
-What to do?
-===========
-- Anything you want.
-- The most useful (because it's the most boring) would be to update print_hand.py, update the expected files (testdata/*.found.txt) and create more .found.txt to ensure import processing is running correctly.
-- There's a list of various bugs, deficiencies and important missing features in known_bugs_and_planned_features.txt.
-- In the main GUI there's various menu points marked with todo - all of these will have to be done eventually.
-
-If you want to take a look at coding-style.txt.
-Ideally use git (see git-instructions.txt for some commands) and let me know where to pull from, alternatively feel free to send patches or even just changed file in whatever code layout or naming convention you like best. I will, of course, still give you full credit.
-
-Contact/Communication
-=====================
-If you start working on something please open a bug or feature request at sf to avoid someone else from doing the same
-Please see readme-overview
-
-Dependencies
-============
-Please let me know before you add any new dependencies and ensure that they are source-compatible between *nix and Windows
-
-Code/File/Class Structure
-=========================
-Basically the code runs like this
-
-fpdb.py -> bulk importer tab (import_threaded.py) -> fpdb_import.py -> fpdb_parse_logic.py -> fpdb_save_to_db.py
-or
-fpdb.py -> table viewer tab (table_viewer.py) (todo: -> libTableViewer)
-
-All files call the simple methods that I just collected in fpdb_simple.py, to abstract the other files off the nitty gritty details as I was learning python.
-I'm currently working on (amongst other things) integrating everything into the fpdb.py GUI with a view to allow easy creation of a CLI client, too.
-
-Also see filelist.txt.
-
-How to Commit
-=============
-Please make sure you read and accept the copyright policy as stated in this file. Then see git-instructions.txt. Don't get me wrong, I hate all this legalese, but unfortunately it's kinda necessary.
-
-Copyright/Licensing
-===================
-Copyright by default is handled on a per-file basis. If you send in a patch or make a commit to an existing file it is done on the understanding that you transfer all rights (as far as legally possible in your jurisdiction) to the current copyright holder of that file, unless otherwise stated. If you create a new file please ensure to include a copyright and license statement.
-
-The licenses used by this project are the AGPL3 for code and FDL1.2 for documentation. See readme-overview.txt for reasons and if you wish to use fpdb with different licensing.
-
-Preferred File Formats
-======================
-Preferred: Where possible simple text-based formats, e.g. plain text (with Unix end of line char) or (X)HTML. Preferred picture format is PNG. IE-compability for HTML files is optional as IE was never meant to be a real web browser, if it were they would've implemented web standards.
-
-Also good: Other free and open formats, e.g. ODF.
-
-Not good: Any format that doesn't have full documentation freely and publicly available with a proper license for anyone to implement it. Sadly, Microsoft has chosen not fulfil these requirements for ISO MS OOXML to become a truly open standard.
-
-License (of this file)
-=======
-Trademarks of third parties have been used under Fair Use or similar laws.
-
-Copyright 2008 Steffen Jobbagy-Felso
-Permission is granted to copy, distribute and/or modify this
-document under the terms of the GNU Free Documentation License,
-Version 1.2 as published by the Free Software Foundation; with
-no Invariant Sections, no Front-Cover Texts, and with no Back-Cover
-Texts. A copy of the license can be found in fdl-1.2.txt
-
-The program itself is licensed under AGPLv3, see agpl-3.0.txt
+Hi,
+This document is to serve as a little overview (later: full technical doc) for current and prospective developers with:
+a) introduction into the code structure
+b) organisational/legal things
+
+What to do?
+===========
+- Anything you want.
+- The most useful (because it's the most boring) would be to update print_hand.py, update the expected files (testdata/*.found.txt) and create more .found.txt to ensure import processing is running correctly.
+- There's a list of various bugs, deficiencies and important missing features in known_bugs_and_planned_features.txt.
+- In the main GUI there's various menu points marked with todo - all of these will have to be done eventually.
+
+If you want to take a look at coding-style.txt.
+Ideally use git (see git-instructions.txt for some commands) and let me know where to pull from, alternatively feel free to send patches or even just changed file in whatever code layout or naming convention you like best. I will, of course, still give you full credit.
+
+Contact/Communication
+=====================
+If you start working on something please open a bug or feature request at sf to avoid someone else from doing the same
+Please see readme-overview
+
+Dependencies
+============
+Please let me know before you add any new dependencies and ensure that they are source-compatible between *nix and Windows
+
+Code/File/Class Structure
+=========================
+Basically the code runs like this
+
+fpdb.py -> bulk importer tab (import_threaded.py) -> fpdb_import.py -> fpdb_parse_logic.py -> fpdb_save_to_db.py
+or
+fpdb.py -> table viewer tab (table_viewer.py) (todo: -> libTableViewer)
+
+All files call the simple methods that I just collected in fpdb_simple.py, to abstract the other files off the nitty gritty details as I was learning python.
+I'm currently working on (amongst other things) integrating everything into the fpdb.py GUI with a view to allow easy creation of a CLI client, too.
+
+Also see filelist.txt.
+
+How to Commit
+=============
+Please make sure you read and accept the copyright policy as stated in this file. Then see git-instructions.txt. Don't get me wrong, I hate all this legalese, but unfortunately it's kinda necessary.
+
+Copyright/Licensing
+===================
+Copyright by default is handled on a per-file basis. If you send in a patch or make a commit to an existing file it is done on the understanding that you transfer all rights (as far as legally possible in your jurisdiction) to the current copyright holder of that file, unless otherwise stated. If you create a new file please ensure to include a copyright and license statement.
+
+The licenses used by this project are the AGPL3 for code and FDL1.2 for documentation. See readme-overview.txt for reasons and if you wish to use fpdb with different licensing.
+
+Preferred File Formats
+======================
+Preferred: Where possible simple text-based formats, e.g. plain text (with Unix end of line char) or (X)HTML. Preferred picture format is PNG. IE-compability for HTML files is optional as IE was never meant to be a real web browser, if it were they would've implemented web standards.
+
+Also good: Other free and open formats, e.g. ODF.
+
+Not good: Any format that doesn't have full documentation freely and publicly available with a proper license for anyone to implement it. Sadly, Microsoft has chosen not fulfil these requirements for ISO MS OOXML to become a truly open standard.
+
+License (of this file)
+=======
+Trademarks of third parties have been used under Fair Use or similar laws.
+
+Copyright 2008 Steffen Jobbagy-Felso
+Permission is granted to copy, distribute and/or modify this
+document under the terms of the GNU Free Documentation License,
+Version 1.2 as published by the Free Software Foundation; with
+no Invariant Sections, no Front-Cover Texts, and with no Back-Cover
+Texts. A copy of the license can be found in fdl-1.2.txt
+
+The program itself is licensed under AGPLv3, see agpl-3.0.txt
diff --git a/ignore-me_perl6/RunFpdbCLI.perl6 b/ignore-me_perl6/RunFpdbCLI.perl6
index 90401cdf..dbbad9a7 100644
--- a/ignore-me_perl6/RunFpdbCLI.perl6
+++ b/ignore-me_perl6/RunFpdbCLI.perl6
@@ -1,29 +1,29 @@
-#!/usr/bin/pugs
-
-#Copyright 2008 Steffen Jobbagy-Felso
-#This program is free software: you can redistribute it and/or modify
-#it under the terms of the GNU Affero General Public License as published by
-#the Free Software Foundation, version 3 of the License.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU Affero General Public License
-#along with this program. If not, see .
-#In the "official" distribution you can find the license in
-#agpl-3.0.txt in the docs folder of the package.
-
-use v6;
-#use strict;
-use LibFpdbImport;
-use LibFpdbShared;
-
-
-my Database $db .= new(:backend, :host, :database, :user, :password);
-#todo: below doesnt work
-my Importer $imp .= new(:db($db), :filename);
-#perlbug?: adding another named argument that isnt listed in the constructor gave very weird error.
-say $imp;
-
+#!/usr/bin/pugs
+
+#Copyright 2008 Steffen Jobbagy-Felso
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU Affero General Public License as published by
+#the Free Software Foundation, version 3 of the License.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU Affero General Public License
+#along with this program. If not, see .
+#In the "official" distribution you can find the license in
+#agpl-3.0.txt in the docs folder of the package.
+
+use v6;
+#use strict;
+use LibFpdbImport;
+use LibFpdbShared;
+
+
+my Database $db .= new(:backend, :host, :database, :user, :password);
+#todo: below doesnt work
+my Importer $imp .= new(:db($db), :filename);
+#perlbug?: adding another named argument that isnt listed in the constructor gave very weird error.
+say $imp;
+
diff --git a/pyfpdb/CarbonToFpdb.py b/pyfpdb/CarbonToFpdb.py
new file mode 100644
index 00000000..cf9fc8d3
--- /dev/null
+++ b/pyfpdb/CarbonToFpdb.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+# Copyright 2008, Carl Gherardi
+
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+########################################################################
+
+# Standard Library modules
+import Configuration
+import traceback
+import sys
+import re
+import xml.dom.minidom
+from xml.dom.minidom import Node
+from HandHistoryConverter import HandHistoryConverter
+
+# Carbon format looks like:
+
+# 1)
+# 2)
+# 3)
+#
+# ...
+# 4)
+#
+#
+# 5)
+#
+# 6)
+#
+# ....
+#
+
+# The full sequence for a NHLE cash game is:
+# BLINDS, PREFLOP, POSTFLOP, POSTTURN, POSTRIVER, SHOWDOWN, END_OF_GAME
+# This sequence can be terminated after BLINDS at any time by END_OF_FOLDED_GAME
+
+
+class CarbonPoker(HandHistoryConverter):
+ def __init__(self, config, filename):
+ print "Initialising Carbon Poker converter class"
+ HandHistoryConverter.__init__(self, config, filename, "Carbon") # Call super class init
+ self.setFileType("xml")
+
+ def readSupportedGames(self):
+ pass
+ def determineGameType(self):
+ gametype = []
+ desc_node = self.doc.getElementsByTagName("description")
+ #TODO: no examples of non ring type yet
+ gametype = gametype + ["ring"]
+ type = desc_node[0].getAttribute("type")
+ if(type == "Holdem"):
+ gametype = gametype + ["hold"]
+ else:
+ print "Unknown gametype: '%s'" % (type)
+
+ stakes = desc_node[0].getAttribute("stakes")
+ #TODO: no examples of anything except nlhe
+ m = re.match('(?PNo Limit)\s\(\$?(?P[.0-9]+)/\$?(?P[.0-9]+)\)', stakes)
+
+ if(m.group('LIMIT') == "No Limit"):
+ gametype = gametype + ["nl"]
+
+ gametype = gametype + [self.float2int(m.group('SB'))]
+ gametype = gametype + [self.float2int(m.group('BB'))]
+
+ return gametype
+
+ def readPlayerStacks(self):
+ pass
+ def readBlinds(self):
+ pass
+ def readAction(self):
+ pass
+
+ # Override read function as xml.minidom barfs on the Carbon layout
+ # This is pretty dodgy
+ def readFile(self, filename):
+ print "Carbon: Reading file: '%s'" %(filename)
+ infile=open(filename, "rU")
+ self.obs = infile.read()
+ infile.close()
+ self.obs = "\n" + self.obs + " "
+ try:
+ doc = xml.dom.minidom.parseString(self.obs)
+ self.doc = doc
+ except:
+ traceback.print_exc(file=sys.stderr)
+
+if __name__ == "__main__":
+ c = Configuration.Config()
+ e = CarbonPoker(c, "regression-test-files/carbon-poker/Niagara Falls (15245216).xml")
+ e.processFile()
+ print str(e)
+
diff --git a/pyfpdb/CliFpdb.py b/pyfpdb/CliFpdb.py
index 47649728..4e2a4361 100644
--- a/pyfpdb/CliFpdb.py
+++ b/pyfpdb/CliFpdb.py
@@ -53,7 +53,7 @@ if __name__ == "__main__":
(options, sys.argv) = parser.parse_args()
- settings={'imp-callFpdbHud':False, 'db-backend':2}
+ settings={'callFpdbHud':False, 'db-backend':2}
settings['db-host']=options.server
settings['db-user']=options.user
settings['db-password']=options.password
diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py
index 3b5c6d70..3bcaaa91 100644
--- a/pyfpdb/Configuration.py
+++ b/pyfpdb/Configuration.py
@@ -1,641 +1,640 @@
-#!/usr/bin/env python
-"""Configuration.py
-
-Handles HUD configuration files.
-"""
-# Copyright 2008, 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 string
-import traceback
-import shutil
-import xml.dom.minidom
-from xml.dom.minidom import Node
-
-class Layout:
- def __init__(self, max):
- self.max = int(max)
- self.location = []
- for i in range(self.max + 1): self.location.append(None)
-
- def __str__(self):
- temp = " Layout = %d max, width= %d, height = %d, fav_seat = %d\n" % (self.max, self.width, self.height, self.fav_seat)
- temp = temp + " Locations = "
- for i in range(1, len(self.location)):
- temp = temp + "(%d,%d)" % self.location[i]
-
- return temp + "\n"
-
-class Site:
- def __init__(self, node):
- self.site_name = node.getAttribute("site_name")
- self.table_finder = node.getAttribute("table_finder")
- self.screen_name = node.getAttribute("screen_name")
- self.site_path = node.getAttribute("site_path")
- self.HH_path = node.getAttribute("HH_path")
- self.decoder = node.getAttribute("decoder")
- self.hudopacity = node.getAttribute("hudopacity")
- self.hudbgcolor = node.getAttribute("bgcolor")
- self.hudfgcolor = node.getAttribute("fgcolor")
- self.converter = node.getAttribute("converter")
- self.enabled = node.getAttribute("enabled")
- self.aux_window = node.getAttribute("aux_window")
- self.layout = {}
-
- for layout_node in node.getElementsByTagName('layout'):
- max = int( layout_node.getAttribute('max') )
- lo = Layout(max)
- lo.fav_seat = int( layout_node.getAttribute('fav_seat') )
- lo.width = int( layout_node.getAttribute('width') )
- lo.height = int( layout_node.getAttribute('height') )
-
- for location_node in layout_node.getElementsByTagName('location'):
- lo.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y')))
-
- self.layout[lo.max] = lo
-
- def __str__(self):
- temp = "Site = " + self.site_name + "\n"
- for key in dir(self):
- if key.startswith('__'): continue
- if key == 'layout': continue
- value = getattr(self, key)
- if callable(value): continue
- temp = temp + ' ' + key + " = " + str(value) + "\n"
-
- for layout in self.layout:
- temp = temp + "%s" % self.layout[layout]
-
- return temp
-
-class Stat:
- def __init__(self):
- pass
-
- def __str__(self):
- temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s, popup = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click, self.popup)
- return temp
-
-class Game:
- def __init__(self, node):
- self.game_name = node.getAttribute("game_name")
- self.db = node.getAttribute("db")
- self.rows = int( node.getAttribute("rows") )
- self.cols = int( node.getAttribute("cols") )
- self.aux = node.getAttribute("aux")
-
- self.stats = {}
- for stat_node in node.getElementsByTagName('stat'):
- stat = Stat()
- stat.stat_name = stat_node.getAttribute("stat_name")
- stat.row = int( stat_node.getAttribute("row") )
- stat.col = int( stat_node.getAttribute("col") )
- stat.tip = stat_node.getAttribute("tip")
- stat.click = stat_node.getAttribute("click")
- stat.popup = stat_node.getAttribute("popup")
- stat.hudprefix = stat_node.getAttribute("hudprefix")
- stat.hudsuffix = stat_node.getAttribute("hudsuffix")
-
- self.stats[stat.stat_name] = stat
-
- def __str__(self):
- temp = "Game = " + self.game_name + "\n"
- temp = temp + " db = %s\n" % self.db
- temp = temp + " rows = %d\n" % self.rows
- temp = temp + " cols = %d\n" % self.cols
- temp = temp + " aux = %s\n" % self.aux
-
- for stat in self.stats.keys():
- temp = temp + "%s" % self.stats[stat]
-
- return temp
-
-class Database:
- def __init__(self, node):
- self.db_name = node.getAttribute("db_name")
- self.db_server = node.getAttribute("db_server")
- self.db_ip = node.getAttribute("db_ip")
- self.db_user = node.getAttribute("db_user")
- self.db_type = node.getAttribute("db_type")
- self.db_pass = node.getAttribute("db_pass")
-
- def __str__(self):
- temp = 'Database = ' + self.db_name + '\n'
- for key in dir(self):
- if key.startswith('__'): continue
- value = getattr(self, key)
- if callable(value): continue
- temp = temp + ' ' + key + " = " + value + "\n"
- return temp
-
-class Aux_window:
- def __init__(self, node):
- for (name, value) in node.attributes.items():
- setattr(self, name, value)
-# self.name = node.getAttribute("mw_name")
-# self.cards = node.getAttribute("deck")
-# self.card_wd = node.getAttribute("card_wd")
-# self.card_ht = node.getAttribute("card_ht")
-# self.rows = node.getAttribute("rows")
-# self.cols = node.getAttribute("cols")
-# self.format = node.getAttribute("stud")
-
- def __str__(self):
- temp = 'Aux = ' + self.name + "\n"
- for key in dir(self):
- if key.startswith('__'): continue
- value = getattr(self, key)
- if callable(value): continue
- temp = temp + ' ' + key + " = " + value + "\n"
- return temp
-
-class Popup:
- def __init__(self, node):
- self.name = node.getAttribute("pu_name")
- self.pu_stats = []
- for stat_node in node.getElementsByTagName('pu_stat'):
- self.pu_stats.append(stat_node.getAttribute("pu_stat_name"))
-
- def __str__(self):
- temp = "Popup = " + self.name + "\n"
- for stat in self.pu_stats:
- temp = temp + " " + stat
- return temp + "\n"
-
-class Import:
- def __init__(self, node):
- self.interval = node.getAttribute("interval")
- self.callFpdbHud = node.getAttribute("callFpdbHud")
- self.hhArchiveBase = node.getAttribute("hhArchiveBase")
-
- def __str__(self):
- return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s" % (self.interval, self.callFpdbHud, self.hhArchiveBase)
-
-class Tv:
- def __init__(self, node):
- self.combinedStealFold = node.getAttribute("combinedStealFold")
- self.combined2B3B = node.getAttribute("combined2B3B")
- self.combinedPostflop = node.getAttribute("combinedPostflop")
-
- def __str__(self):
- return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" %
- (self.combinedStealFold, self.combined2B3B, self.combinedPostflop) )
-
-class Config:
- def __init__(self, file = None):
-
-# "file" is a path to an xml file with the fpdb/HUD configuration
-# we check the existence of "file" and try to recover if it doesn't exist
-
- self.default_config_path = self.get_default_config_path()
- if not file == None: # configuration file path has been passed
- if not os.path.exists(file):
- print "Configuration file %s not found. Using defaults." % (file)
- sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
- file = None
-
- if file == None: # configuration file path not passed or invalid
- file = self.find_config() #Look for a config file in the normal places
-
- if file == None: # no config file in the normal places
- file = self.find_example_config() #Look for an example file to edit
- if not file == None:
- pass
-
- if file == None: # that didn't work either, just die
- print "No HUD_config_xml found. Exiting"
- sys.stderr.write("No HUD_config_xml found. Exiting")
- sys.exit()
-
-# Parse even if there was no real config file found and we are using the example
-# If using the example, we'll edit it later
- try:
- print "Reading configuration file %s\n" % (file)
- doc = xml.dom.minidom.parse(file)
- except:
- print "Error parsing %s. See error log file." % (file)
- traceback.print_exc(file=sys.stderr)
- print "press enter to continue"
- sys.stdin.readline()
- sys.exit()
-
- self.doc = doc
- self.file = file
- self.supported_sites = {}
- self.supported_games = {}
- self.supported_databases = {}
- self.aux_windows = {}
- self.popup_windows = {}
-
-# s_sites = doc.getElementsByTagName("supported_sites")
- for site_node in doc.getElementsByTagName("site"):
- site = Site(node = site_node)
- self.supported_sites[site.site_name] = site
-
-# s_games = doc.getElementsByTagName("supported_games")
- for game_node in doc.getElementsByTagName("game"):
- game = Game(node = game_node)
- self.supported_games[game.game_name] = game
-
-# s_dbs = doc.getElementsByTagName("supported_databases")
- for db_node in doc.getElementsByTagName("database"):
- db = Database(node = db_node)
- self.supported_databases[db.db_name] = db
-
-# s_dbs = doc.getElementsByTagName("mucked_windows")
- for aw_node in doc.getElementsByTagName("aw"):
- aw = Aux_window(node = aw_node)
- self.aux_windows[aw.name] = aw
-
-# s_dbs = doc.getElementsByTagName("popup_windows")
- for pu_node in doc.getElementsByTagName("pu"):
- pu = Popup(node = pu_node)
- self.popup_windows[pu.name] = pu
-
- for imp_node in doc.getElementsByTagName("import"):
- imp = Import(node = imp_node)
- self.imp = imp
-
- for tv_node in doc.getElementsByTagName("tv"):
- tv = Tv(node = tv_node)
- self.tv = tv
-
- db = self.get_db_parameters('fpdb')
- if db['db-password'] == 'YOUR MYSQL PASSWORD':
- df_file = self.find_default_conf()
- if df_file == None: # this is bad
- pass
- else:
- df_parms = self.read_default_conf(df_file)
- self.set_db_parameters(db_name = 'fpdb', db_ip = df_parms['db-host'],
- db_user = df_parms['db-user'],
- db_pass = df_parms['db-password'])
- self.save(file=os.path.join(self.default_config_path, "HUD_config.xml"))
-
-
- def find_config(self):
- """Looks in cwd and in self.default_config_path for a config file."""
- if os.path.exists('HUD_config.xml'): # there is a HUD_config in the cwd
- file = 'HUD_config.xml' # so we use it
- else: # no HUD_config in the cwd, look where it should be in the first place
- config_path = os.path.join(self.default_config_path, 'HUD_config.xml')
- if os.path.exists(config_path):
- file = config_path
- else:
- file = None
- return file
-
- def get_default_config_path(self):
- """Returns the path where the fpdb config file _should_ be stored."""
- if os.name == 'posix':
- config_path = os.path.join(os.path.expanduser("~"), '.fpdb')
- elif os.name == 'nt':
- config_path = os.path.join(os.environ["APPDATA"], 'fpdb')
- else: config_path = None
- return config_path
-
-
- def find_default_conf(self):
- if os.name == 'posix':
- config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'default.conf')
- elif os.name == 'nt':
- config_path = os.path.join(os.environ["APPDATA"], 'fpdb', 'default.conf')
- else: config_path = False
-
- if config_path and os.path.exists(config_path):
- file = config_path
- else:
- file = None
- return file
-
- def read_default_conf(self, file):
- parms = {}
- fh = open(file, "r")
- for line in fh:
- line = string.strip(line)
- (key, value) = line.split('=')
- parms[key] = value
- fh.close
- return parms
-
- def find_example_config(self):
- if os.path.exists('HUD_config.xml.example'): # there is a HUD_config in the cwd
- file = 'HUD_config.xml.example' # so we use it
- print "No HUD_config.xml found, using HUD_config.xml.example.\n", \
- "A HUD_config.xml will be written. You will probably have to edit it."
- sys.stderr.write("No HUD_config.xml found, using HUD_config.xml.example.\n" + \
- "A HUD_config.xml will be written. You will probably have to edit it.")
- else:
- file = None
- return file
-
- def get_site_node(self, site):
- for site_node in self.doc.getElementsByTagName("site"):
- if site_node.getAttribute("site_name") == site:
- return site_node
-
- def get_db_node(self, db_name):
- for db_node in self.doc.getElementsByTagName("database"):
- if db_node.getAttribute("db_name") == db_name:
- return db_node
- return None
-
- def get_layout_node(self, site_node, layout):
- for layout_node in site_node.getElementsByTagName("layout"):
- if layout_node.getAttribute("max") == None:
- return None
- if int( layout_node.getAttribute("max") ) == int( layout ):
- return layout_node
-
- def get_location_node(self, layout_node, seat):
- for location_node in layout_node.getElementsByTagName("location"):
- if int( location_node.getAttribute("seat") ) == int( seat ):
- return location_node
-
- def save(self, file = None):
- if not file == None:
- f = open(file, 'w')
- self.doc.writexml(f)
- f.close()
- else:
- shutil.move(self.file, self.file+".backup")
- f = open(self.file, 'w')
- self.doc.writexml(f)
- f.close
-
- def edit_layout(self, site_name, max, width = None, height = None,
- fav_seat = None, locations = None):
- site_node = self.get_site_node(site_name)
- layout_node = self.get_layout_node(site_node, max)
- if layout_node == None: return
- for i in range(1, max + 1):
- location_node = self.get_location_node(layout_node, i)
- location_node.setAttribute("x", str( locations[i-1][0] ))
- location_node.setAttribute("y", str( locations[i-1][1] ))
- self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] )
-
- def get_db_parameters(self, name = None):
- if name == None: name = 'fpdb'
- db = {}
- try:
- db['db-databaseName'] = name
- db['db-host'] = self.supported_databases[name].db_ip
- db['db-user'] = self.supported_databases[name].db_user
- db['db-password'] = self.supported_databases[name].db_pass
- db['db-server'] = self.supported_databases[name].db_server
- if string.lower(self.supported_databases[name].db_server) == 'mysql':
- db['db-backend'] = 2
- elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
- db['db-backend'] = 3
- else: db['db-backend'] = None # this is big trouble
- except:
- pass
- return db
-
- def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
- db_pass = None, db_server = None, db_type = None):
- db_node = self.get_db_node(db_name)
- if not db_node == None:
- if not db_ip == None: db_node.setAttribute("db_ip", db_ip)
- if not db_user == None: db_node.setAttribute("db_user", db_user)
- if not db_pass == None: db_node.setAttribute("db_pass", db_pass)
- if not db_server == None: db_node.setAttribute("db_server", db_server)
- if not db_type == None: db_node.setAttribute("db_type", db_type)
- if self.supported_databases.has_key(db_name):
- if not db_ip == None: self.supported_databases[db_name].dp_ip = db_ip
- if not db_user == None: self.supported_databases[db_name].dp_user = db_user
- if not db_pass == None: self.supported_databases[db_name].dp_pass = db_pass
- if not db_server == None: self.supported_databases[db_name].dp_server = db_server
- if not db_type == None: self.supported_databases[db_name].dp_type = db_type
- return
-
- def get_tv_parameters(self):
- tv = {}
- try:
- tv['combinedStealFold'] = self.tv.combinedStealFold
- tv['combined2B3B'] = self.tv.combined2B3B
- tv['combinedPostflop'] = self.tv.combinedPostflop
- except: # Default tv parameters
- tv['combinedStealFold'] = True
- tv['combined2B3B'] = True
- tv['combinedPostflop'] = True
- return tv
-
- def get_import_parameters(self):
- imp = {}
- try:
- imp['callFpdbHud'] = self.callFpdbHud
- imp['interval'] = self.interval
- imp['hhArchiveBase'] = self.hhArchiveBase
- except: # Default params
- imp['callFpdbHud'] = True
- imp['interval'] = 10
- imp['hhArchiveBase'] = "~/.fpdb/HandHistories/"
- return imp
-
- def get_default_paths(self, site = "PokerStars"):
- paths = {}
- try:
- paths['hud-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path)
- paths['bulkImport-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path)
- except:
- paths['hud-defaultPath'] = "default"
- paths['bulkImport-defaultPath'] = "default"
- return paths
-
- def get_default_colors(self, site = "PokerStars"):
- colors = {}
- if self.supported_sites[site].hudopacity == "":
- colors['hudopacity'] = 0.90
- else:
- colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
- if self.supported_sites[site].hudbgcolor == "":
- colors['hudbgcolor'] = "#FFFFFF"
- else:
- colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
- if self.supported_sites[site].hudfgcolor == "":
- colors['hudfgcolor'] = "#000000"
- else:
- colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
- return colors
-
- def get_locations(self, site = "PokerStars", max = "8"):
-
- try:
- locations = self.supported_sites[site].layout[max].location
- except:
- locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346),
- (586, 393), (421, 440), (267, 440), ( 0, 361),
- ( 0, 280), (121, 280), ( 46, 30) )
- return locations
-
- def get_supported_sites(self):
- """Returns the list of supported sites."""
- return self.supported_sites.keys()
-
- def get_site_parameters(self, site):
- """Returns a dict of the site parameters for the specified site"""
- if not self.supported_sites.has_key(site):
- return None
- parms = {}
- parms["converter"] = self.supported_sites[site].converter
- parms["decoder"] = self.supported_sites[site].decoder
- parms["hudbgcolor"] = self.supported_sites[site].hudbgcolor
- parms["hudfgcolor"] = self.supported_sites[site].hudfgcolor
- parms["hudopacity"] = self.supported_sites[site].hudopacity
- parms["screen_name"] = self.supported_sites[site].screen_name
- parms["site_path"] = self.supported_sites[site].site_path
- parms["table_finder"] = self.supported_sites[site].table_finder
- parms["HH_path"] = self.supported_sites[site].HH_path
- parms["site_name"] = self.supported_sites[site].site_name
- parms["enabled"] = self.supported_sites[site].enabled
- parms["aux_window"] = self.supported_sites[site].aux_window
- return parms
-
- def set_site_parameters(self, site_name, converter = None, decoder = None,
- hudbgcolor = None, hudfgcolor = None,
- hudopacity = None, screen_name = None,
- site_path = None, table_finder = None,
- HH_path = None, enabled = None):
- """Sets the specified site parameters for the specified site."""
- site_node = self.get_site_node(site_name)
- if not db_node == None:
- if not converter == None: site_node.setAttribute("converter", converter)
- if not decoder == None: site_node.setAttribute("decoder", decoder)
- if not hudbgcolor == None: site_node.setAttribute("hudbgcolor", hudbgcolor)
- if not hudfgcolor == None: site_node.setAttribute("hudfgcolor", hudfgcolor)
- if not hudopacity == None: site_node.setAttribute("hudopacity", hudopacity)
- if not screen_name == None: site_node.setAttribute("screen_name", screen_name)
- if not site_path == None: site_node.setAttribute("site_path", site_path)
- if not table_finder == None: site_node.setAttribute("table_finder", table_finder)
- if not HH_path == None: site_node.setAttribute("HH_path", HH_path)
- if not enabled == None: site_node.setAttribute("enabled", enabled)
-
- if self.supported_databases.has_key(db_name):
- if not converter == None: self.supported_sites[site].converter = converter
- if not decoder == None: self.supported_sites[site].decoder = decoder
- if not hudbgcolor == None: self.supported_sites[site].hudbgcolor = hudbgcolor
- if not hudfgcolor == None: self.supported_sites[site].hudfgcolor = hudfgcolor
- if not hudopacity == None: self.supported_sites[site].hudopacity = hudopacity
- if not screen_name == None: self.supported_sites[site].screen_name = screen_name
- if not site_path == None: self.supported_sites[site].site_path = site_path
- if not table_finder == None: self.supported_sites[site].table_finder = table_finder
- if not HH_path == None: self.supported_sites[site].HH_path = HH_path
- if not enabled == None: self.supported_sites[site].enabled = enabled
- return
-
- def get_aux_windows(self):
- """Gets the list of mucked window formats in the configuration."""
- mw = []
- for w in self.aux_windows.keys():
- mw.append(w)
- return mw
-
- def get_aux_parameters(self, name):
- """Gets a dict of mucked window parameters from the named mw."""
- param = {}
- if self.aux_windows.has_key(name):
- for key in dir(self.aux_windows[name]):
- if key.startswith('__'): continue
- value = getattr(self.aux_windows[name], key)
- if callable(value): continue
- param[key] = value
-
- return param
- return None
-
- def get_game_parameters(self, name):
- """Get the configuration parameters for the named game."""
- param = {}
- if self.supported_games.has_key(name):
- param['game_name'] = self.supported_games[name].game_name
- param['db'] = self.supported_games[name].db
- param['rows'] = self.supported_games[name].rows
- param['cols'] = self.supported_games[name].cols
- param['aux'] = self.supported_games[name].aux
- return param
-
- def get_supported_games(self):
- """Get the list of supported games."""
- sg = []
- for game in c.supported_games.keys():
- sg.append(c.supported_games[game].game_name)
- return sg
-
-if __name__== "__main__":
- c = Config()
-
- print "\n----------- SUPPORTED SITES -----------"
- for s in c.supported_sites.keys():
- print c.supported_sites[s]
- print "----------- END SUPPORTED SITES -----------"
-
-
- print "\n----------- SUPPORTED GAMES -----------"
- for game in c.supported_games.keys():
- print c.supported_games[game]
- print "----------- END SUPPORTED GAMES -----------"
-
-
- print "\n----------- SUPPORTED DATABASES -----------"
- for db in c.supported_databases.keys():
- print c.supported_databases[db]
- print "----------- END SUPPORTED DATABASES -----------"
-
- print "\n----------- AUX WINDOW FORMATS -----------"
- for w in c.aux_windows.keys():
- print c.aux_windows[w]
- print "----------- END AUX WINDOW FORMATS -----------"
-
- print "\n----------- POPUP WINDOW FORMATS -----------"
- for w in c.popup_windows.keys():
- print c.popup_windows[w]
- print "----------- END POPUP WINDOW FORMATS -----------"
-
- print "\n----------- IMPORT -----------"
- tmp = c.get_import_parameters()
- for param in tmp:
- print " " + str(param) + ": " + str(tmp[param])
- 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.save(file="testout.xml")
-
- print "db = ", c.get_db_parameters()
-# print "tv = ", c.get_tv_parameters()
-# print "imp = ", c.get_import_parameters()
- print "paths = ", c.get_default_paths("PokerStars")
- print "colors = ", c.get_default_colors("PokerStars")
- print "locs = ", c.get_locations("PokerStars", 8)
- for mw in c.get_aux_windows():
- print c.get_aux_parameters(mw)
-
- for site in c.supported_sites.keys():
- print "site = ", site,
- print c.get_site_parameters(site)
-
- for game in c.get_supported_games():
- print c.get_game_parameters(game)
+#!/usr/bin/env python
+"""Configuration.py
+
+Handles HUD configuration files.
+"""
+# Copyright 2008, 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 string
+import traceback
+import shutil
+import xml.dom.minidom
+from xml.dom.minidom import Node
+
+class Layout:
+ def __init__(self, max):
+ self.max = int(max)
+ self.location = []
+ for i in range(self.max + 1): self.location.append(None)
+
+ def __str__(self):
+ temp = " Layout = %d max, width= %d, height = %d, fav_seat = %d\n" % (self.max, self.width, self.height, self.fav_seat)
+ temp = temp + " Locations = "
+ for i in range(1, len(self.location)):
+ temp = temp + "(%d,%d)" % self.location[i]
+
+ return temp + "\n"
+
+class Site:
+ def __init__(self, node):
+ self.site_name = node.getAttribute("site_name")
+ self.table_finder = node.getAttribute("table_finder")
+ self.screen_name = node.getAttribute("screen_name")
+ self.site_path = node.getAttribute("site_path")
+ self.HH_path = node.getAttribute("HH_path")
+ self.decoder = node.getAttribute("decoder")
+ self.hudopacity = node.getAttribute("hudopacity")
+ self.hudbgcolor = node.getAttribute("bgcolor")
+ self.hudfgcolor = node.getAttribute("fgcolor")
+ self.converter = node.getAttribute("converter")
+ self.enabled = node.getAttribute("enabled")
+ self.aux_window = node.getAttribute("aux_window")
+ self.layout = {}
+
+ for layout_node in node.getElementsByTagName('layout'):
+ max = int( layout_node.getAttribute('max') )
+ lo = Layout(max)
+ lo.fav_seat = int( layout_node.getAttribute('fav_seat') )
+ lo.width = int( layout_node.getAttribute('width') )
+ lo.height = int( layout_node.getAttribute('height') )
+
+ for location_node in layout_node.getElementsByTagName('location'):
+ lo.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y')))
+
+ self.layout[lo.max] = lo
+
+ def __str__(self):
+ temp = "Site = " + self.site_name + "\n"
+ for key in dir(self):
+ if key.startswith('__'): continue
+ if key == 'layout': continue
+ value = getattr(self, key)
+ if callable(value): continue
+ temp = temp + ' ' + key + " = " + str(value) + "\n"
+
+ for layout in self.layout:
+ temp = temp + "%s" % self.layout[layout]
+
+ return temp
+
+class Stat:
+ def __init__(self):
+ pass
+
+ def __str__(self):
+ temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s, popup = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click, self.popup)
+ return temp
+
+class Game:
+ def __init__(self, node):
+ self.game_name = node.getAttribute("game_name")
+ self.db = node.getAttribute("db")
+ self.rows = int( node.getAttribute("rows") )
+ self.cols = int( node.getAttribute("cols") )
+ self.aux = node.getAttribute("aux")
+
+ self.stats = {}
+ for stat_node in node.getElementsByTagName('stat'):
+ stat = Stat()
+ stat.stat_name = stat_node.getAttribute("stat_name")
+ stat.row = int( stat_node.getAttribute("row") )
+ stat.col = int( stat_node.getAttribute("col") )
+ stat.tip = stat_node.getAttribute("tip")
+ stat.click = stat_node.getAttribute("click")
+ stat.popup = stat_node.getAttribute("popup")
+ stat.hudprefix = stat_node.getAttribute("hudprefix")
+ stat.hudsuffix = stat_node.getAttribute("hudsuffix")
+ stat.hudcolor = stat_node.getAttribute("hudcolor")
+
+ self.stats[stat.stat_name] = stat
+
+ def __str__(self):
+ temp = "Game = " + self.game_name + "\n"
+ temp = temp + " db = %s\n" % self.db
+ temp = temp + " rows = %d\n" % self.rows
+ temp = temp + " cols = %d\n" % self.cols
+ temp = temp + " aux = %s\n" % self.aux
+
+ for stat in self.stats.keys():
+ temp = temp + "%s" % self.stats[stat]
+
+ return temp
+
+class Database:
+ def __init__(self, node):
+ self.db_name = node.getAttribute("db_name")
+ self.db_server = node.getAttribute("db_server")
+ self.db_ip = node.getAttribute("db_ip")
+ self.db_user = node.getAttribute("db_user")
+ self.db_type = node.getAttribute("db_type")
+ self.db_pass = node.getAttribute("db_pass")
+
+ def __str__(self):
+ temp = 'Database = ' + self.db_name + '\n'
+ for key in dir(self):
+ if key.startswith('__'): continue
+ value = getattr(self, key)
+ if callable(value): continue
+ temp = temp + ' ' + key + " = " + value + "\n"
+ return temp
+
+class Aux_window:
+ def __init__(self, node):
+ for (name, value) in node.attributes.items():
+ setattr(self, name, value)
+# self.name = node.getAttribute("mw_name")
+# self.cards = node.getAttribute("deck")
+# self.card_wd = node.getAttribute("card_wd")
+# self.card_ht = node.getAttribute("card_ht")
+# self.rows = node.getAttribute("rows")
+# self.cols = node.getAttribute("cols")
+# self.format = node.getAttribute("stud")
+
+ def __str__(self):
+ temp = 'Aux = ' + self.name + "\n"
+ for key in dir(self):
+ if key.startswith('__'): continue
+ value = getattr(self, key)
+ if callable(value): continue
+ temp = temp + ' ' + key + " = " + value + "\n"
+ return temp
+
+class Popup:
+ def __init__(self, node):
+ self.name = node.getAttribute("pu_name")
+ self.pu_stats = []
+ for stat_node in node.getElementsByTagName('pu_stat'):
+ self.pu_stats.append(stat_node.getAttribute("pu_stat_name"))
+
+ def __str__(self):
+ temp = "Popup = " + self.name + "\n"
+ for stat in self.pu_stats:
+ temp = temp + " " + stat
+ return temp + "\n"
+
+class Import:
+ def __init__(self, node):
+ self.interval = node.getAttribute("interval")
+ self.callFpdbHud = node.getAttribute("callFpdbHud")
+ self.hhArchiveBase = node.getAttribute("hhArchiveBase")
+
+ def __str__(self):
+ return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s" % (self.interval, self.callFpdbHud, self.hhArchiveBase)
+
+class Tv:
+ def __init__(self, node):
+ self.combinedStealFold = node.getAttribute("combinedStealFold")
+ self.combined2B3B = node.getAttribute("combined2B3B")
+ self.combinedPostflop = node.getAttribute("combinedPostflop")
+
+ def __str__(self):
+ return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" %
+ (self.combinedStealFold, self.combined2B3B, self.combinedPostflop) )
+
+class Config:
+ def __init__(self, file = None):
+
+# "file" is a path to an xml file with the fpdb/HUD configuration
+# we check the existence of "file" and try to recover if it doesn't exist
+
+ self.default_config_path = self.get_default_config_path()
+ if not file == None: # configuration file path has been passed
+ if not os.path.exists(file):
+ print "Configuration file %s not found. Using defaults." % (file)
+ sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
+ file = None
+
+ if file == None: # configuration file path not passed or invalid
+ file = self.find_config() #Look for a config file in the normal places
+
+ if file == None: # no config file in the normal places
+ file = self.find_example_config() #Look for an example file to edit
+ if not file == None:
+ pass
+
+ if file == None: # that didn't work either, just die
+ print "No HUD_config_xml found. Exiting"
+ sys.stderr.write("No HUD_config_xml found. Exiting")
+ sys.exit()
+
+# Parse even if there was no real config file found and we are using the example
+# If using the example, we'll edit it later
+ try:
+ print "Reading configuration file %s\n" % (file)
+ doc = xml.dom.minidom.parse(file)
+ except:
+ print "Error parsing %s. See error log file." % (file)
+ traceback.print_exc(file=sys.stderr)
+ print "press enter to continue"
+ sys.stdin.readline()
+ sys.exit()
+
+ self.doc = doc
+ self.file = file
+ self.supported_sites = {}
+ self.supported_games = {}
+ self.supported_databases = {}
+ self.aux_windows = {}
+ self.popup_windows = {}
+
+# s_sites = doc.getElementsByTagName("supported_sites")
+ for site_node in doc.getElementsByTagName("site"):
+ site = Site(node = site_node)
+ self.supported_sites[site.site_name] = site
+
+# s_games = doc.getElementsByTagName("supported_games")
+ for game_node in doc.getElementsByTagName("game"):
+ game = Game(node = game_node)
+ self.supported_games[game.game_name] = game
+
+# s_dbs = doc.getElementsByTagName("supported_databases")
+ for db_node in doc.getElementsByTagName("database"):
+ db = Database(node = db_node)
+ self.supported_databases[db.db_name] = db
+
+# s_dbs = doc.getElementsByTagName("mucked_windows")
+ for aw_node in doc.getElementsByTagName("aw"):
+ aw = Aux_window(node = aw_node)
+ self.aux_windows[aw.name] = aw
+
+# s_dbs = doc.getElementsByTagName("popup_windows")
+ for pu_node in doc.getElementsByTagName("pu"):
+ pu = Popup(node = pu_node)
+ self.popup_windows[pu.name] = pu
+
+ for imp_node in doc.getElementsByTagName("import"):
+ imp = Import(node = imp_node)
+ self.imp = imp
+
+ for tv_node in doc.getElementsByTagName("tv"):
+ tv = Tv(node = tv_node)
+ self.tv = tv
+
+ db = self.get_db_parameters('fpdb')
+ if db['db-password'] == 'YOUR MYSQL PASSWORD':
+ df_file = self.find_default_conf()
+ if df_file == None: # this is bad
+ pass
+ else:
+ df_parms = self.read_default_conf(df_file)
+ self.set_db_parameters(db_name = 'fpdb', db_ip = df_parms['db-host'],
+ db_user = df_parms['db-user'],
+ db_pass = df_parms['db-password'])
+ self.save(file=os.path.join(self.default_config_path, "HUD_config.xml"))
+
+
+ def find_config(self):
+ """Looks in cwd and in self.default_config_path for a config file."""
+ if os.path.exists('HUD_config.xml'): # there is a HUD_config in the cwd
+ file = 'HUD_config.xml' # so we use it
+ else: # no HUD_config in the cwd, look where it should be in the first place
+ config_path = os.path.join(self.default_config_path, 'HUD_config.xml')
+ if os.path.exists(config_path):
+ file = config_path
+ else:
+ file = None
+ return file
+
+ def get_default_config_path(self):
+ """Returns the path where the fpdb config file _should_ be stored."""
+ if os.name == 'posix':
+ config_path = os.path.join(os.path.expanduser("~"), '.fpdb')
+ elif os.name == 'nt':
+ config_path = os.path.join(os.environ["APPDATA"], 'fpdb')
+ else: config_path = None
+ return config_path
+
+
+ def find_default_conf(self):
+ if os.name == 'posix':
+ config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'default.conf')
+ elif os.name == 'nt':
+ config_path = os.path.join(os.environ["APPDATA"], 'fpdb', 'default.conf')
+ else: config_path = False
+
+ if config_path and os.path.exists(config_path):
+ file = config_path
+ else:
+ file = None
+ return file
+
+ def read_default_conf(self, file):
+ parms = {}
+ fh = open(file, "r")
+ for line in fh:
+ line = string.strip(line)
+ (key, value) = line.split('=')
+ parms[key] = value
+ fh.close
+ return parms
+
+ def find_example_config(self):
+ if os.path.exists('HUD_config.xml.example'): # there is a HUD_config in the cwd
+ file = 'HUD_config.xml.example' # so we use it
+ print "No HUD_config.xml found, using HUD_config.xml.example.\n", \
+ "A HUD_config.xml will be written. You will probably have to edit it."
+ sys.stderr.write("No HUD_config.xml found, using HUD_config.xml.example.\n" + \
+ "A HUD_config.xml will be written. You will probably have to edit it.")
+ else:
+ file = None
+ return file
+
+ def get_site_node(self, site):
+ for site_node in self.doc.getElementsByTagName("site"):
+ if site_node.getAttribute("site_name") == site:
+ return site_node
+
+ def get_db_node(self, db_name):
+ for db_node in self.doc.getElementsByTagName("database"):
+ if db_node.getAttribute("db_name") == db_name:
+ return db_node
+ return None
+
+ def get_layout_node(self, site_node, layout):
+ for layout_node in site_node.getElementsByTagName("layout"):
+ if layout_node.getAttribute("max") == None:
+ return None
+ if int( layout_node.getAttribute("max") ) == int( layout ):
+ return layout_node
+
+ def get_location_node(self, layout_node, seat):
+ for location_node in layout_node.getElementsByTagName("location"):
+ if int( location_node.getAttribute("seat") ) == int( seat ):
+ return location_node
+
+ def save(self, file = None):
+ if not file == None:
+ f = open(file, 'w')
+ self.doc.writexml(f)
+ f.close()
+ else:
+ shutil.move(self.file, self.file+".backup")
+ f = open(self.file, 'w')
+ self.doc.writexml(f)
+ f.close
+
+ def edit_layout(self, site_name, max, width = None, height = None,
+ fav_seat = None, locations = None):
+ site_node = self.get_site_node(site_name)
+ layout_node = self.get_layout_node(site_node, max)
+ if layout_node == None: return
+ for i in range(1, max + 1):
+ location_node = self.get_location_node(layout_node, i)
+ location_node.setAttribute("x", str( locations[i-1][0] ))
+ location_node.setAttribute("y", str( locations[i-1][1] ))
+ self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] )
+
+ def get_db_parameters(self, name = None):
+ if name == None: name = 'fpdb'
+ db = {}
+ try:
+ db['db-databaseName'] = name
+ db['db-host'] = self.supported_databases[name].db_ip
+ db['db-user'] = self.supported_databases[name].db_user
+ db['db-password'] = self.supported_databases[name].db_pass
+ db['db-server'] = self.supported_databases[name].db_server
+ if string.lower(self.supported_databases[name].db_server) == 'mysql':
+ db['db-backend'] = 2
+ elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
+ db['db-backend'] = 3
+ else: db['db-backend'] = None # this is big trouble
+ except:
+ pass
+ return db
+
+ def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
+ db_pass = None, db_server = None, db_type = None):
+ db_node = self.get_db_node(db_name)
+ if not db_node == None:
+ if not db_ip == None: db_node.setAttribute("db_ip", db_ip)
+ if not db_user == None: db_node.setAttribute("db_user", db_user)
+ if not db_pass == None: db_node.setAttribute("db_pass", db_pass)
+ if not db_server == None: db_node.setAttribute("db_server", db_server)
+ if not db_type == None: db_node.setAttribute("db_type", db_type)
+ if self.supported_databases.has_key(db_name):
+ if not db_ip == None: self.supported_databases[db_name].dp_ip = db_ip
+ if not db_user == None: self.supported_databases[db_name].dp_user = db_user
+ if not db_pass == None: self.supported_databases[db_name].dp_pass = db_pass
+ if not db_server == None: self.supported_databases[db_name].dp_server = db_server
+ if not db_type == None: self.supported_databases[db_name].dp_type = db_type
+ return
+
+ def get_tv_parameters(self):
+ tv = {}
+ try:
+ tv['combinedStealFold'] = self.tv.combinedStealFold
+ tv['combined2B3B'] = self.tv.combined2B3B
+ tv['combinedPostflop'] = self.tv.combinedPostflop
+ except: # Default tv parameters
+ tv['combinedStealFold'] = True
+ tv['combined2B3B'] = True
+ tv['combinedPostflop'] = True
+ return tv
+
+ def get_import_parameters(self):
+ imp = {}
+ try:
+ imp['callFpdbHud'] = self.imp.callFpdbHud
+ imp['interval'] = self.imp.interval
+ imp['hhArchiveBase'] = self.imp.hhArchiveBase
+ except: # Default params
+ imp['callFpdbHud'] = True
+ imp['interval'] = 10
+ imp['hhArchiveBase'] = "~/.fpdb/HandHistories/"
+ return imp
+
+ def get_default_paths(self, site = "PokerStars"):
+ paths = {}
+ try:
+ paths['hud-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path)
+ paths['bulkImport-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path)
+ except:
+ paths['hud-defaultPath'] = "default"
+ paths['bulkImport-defaultPath'] = "default"
+ return paths
+
+ def get_default_colors(self, site = "PokerStars"):
+ colors = {}
+ if self.supported_sites[site].hudopacity == "":
+ colors['hudopacity'] = 0.90
+ else:
+ colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
+ if self.supported_sites[site].hudbgcolor == "":
+ colors['hudbgcolor'] = "#FFFFFF"
+ else:
+ colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
+ if self.supported_sites[site].hudfgcolor == "":
+ colors['hudfgcolor'] = "#000000"
+ else:
+ colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
+ return colors
+
+ def get_locations(self, site = "PokerStars", max = "8"):
+
+ try:
+ locations = self.supported_sites[site].layout[max].location
+ except:
+ locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346),
+ (586, 393), (421, 440), (267, 440), ( 0, 361),
+ ( 0, 280), (121, 280), ( 46, 30) )
+ return locations
+
+ def get_supported_sites(self):
+ """Returns the list of supported sites."""
+ return self.supported_sites.keys()
+
+ def get_site_parameters(self, site):
+ """Returns a dict of the site parameters for the specified site"""
+ if not self.supported_sites.has_key(site):
+ return None
+ parms = {}
+ parms["converter"] = self.supported_sites[site].converter
+ parms["decoder"] = self.supported_sites[site].decoder
+ parms["hudbgcolor"] = self.supported_sites[site].hudbgcolor
+ parms["hudfgcolor"] = self.supported_sites[site].hudfgcolor
+ parms["hudopacity"] = self.supported_sites[site].hudopacity
+ parms["screen_name"] = self.supported_sites[site].screen_name
+ parms["site_path"] = self.supported_sites[site].site_path
+ parms["table_finder"] = self.supported_sites[site].table_finder
+ parms["HH_path"] = self.supported_sites[site].HH_path
+ parms["site_name"] = self.supported_sites[site].site_name
+ parms["enabled"] = self.supported_sites[site].enabled
+ parms["aux_window"] = self.supported_sites[site].aux_window
+ return parms
+
+ def set_site_parameters(self, site_name, converter = None, decoder = None,
+ hudbgcolor = None, hudfgcolor = None,
+ hudopacity = None, screen_name = None,
+ site_path = None, table_finder = None,
+ HH_path = None, enabled = None):
+ """Sets the specified site parameters for the specified site."""
+ site_node = self.get_site_node(site_name)
+ if not db_node == None:
+ if not converter == None: site_node.setAttribute("converter", converter)
+ if not decoder == None: site_node.setAttribute("decoder", decoder)
+ if not hudbgcolor == None: site_node.setAttribute("hudbgcolor", hudbgcolor)
+ if not hudfgcolor == None: site_node.setAttribute("hudfgcolor", hudfgcolor)
+ if not hudopacity == None: site_node.setAttribute("hudopacity", hudopacity)
+ if not screen_name == None: site_node.setAttribute("screen_name", screen_name)
+ if not site_path == None: site_node.setAttribute("site_path", site_path)
+ if not table_finder == None: site_node.setAttribute("table_finder", table_finder)
+ if not HH_path == None: site_node.setAttribute("HH_path", HH_path)
+ if not enabled == None: site_node.setAttribute("enabled", enabled)
+
+ if self.supported_databases.has_key(db_name):
+ if not converter == None: self.supported_sites[site].converter = converter
+ if not decoder == None: self.supported_sites[site].decoder = decoder
+ if not hudbgcolor == None: self.supported_sites[site].hudbgcolor = hudbgcolor
+ if not hudfgcolor == None: self.supported_sites[site].hudfgcolor = hudfgcolor
+ if not hudopacity == None: self.supported_sites[site].hudopacity = hudopacity
+ if not screen_name == None: self.supported_sites[site].screen_name = screen_name
+ if not site_path == None: self.supported_sites[site].site_path = site_path
+ if not table_finder == None: self.supported_sites[site].table_finder = table_finder
+ if not HH_path == None: self.supported_sites[site].HH_path = HH_path
+ if not enabled == None: self.supported_sites[site].enabled = enabled
+ return
+
+ def get_aux_windows(self):
+ """Gets the list of mucked window formats in the configuration."""
+ mw = []
+ for w in self.aux_windows.keys():
+ mw.append(w)
+ return mw
+
+ def get_aux_parameters(self, name):
+ """Gets a dict of mucked window parameters from the named mw."""
+ param = {}
+ if self.aux_windows.has_key(name):
+ for key in dir(self.aux_windows[name]):
+ if key.startswith('__'): continue
+ value = getattr(self.aux_windows[name], key)
+ if callable(value): continue
+ param[key] = value
+
+ return param
+ return None
+
+ def get_game_parameters(self, name):
+ """Get the configuration parameters for the named game."""
+ param = {}
+ if self.supported_games.has_key(name):
+ param['game_name'] = self.supported_games[name].game_name
+ param['db'] = self.supported_games[name].db
+ param['rows'] = self.supported_games[name].rows
+ param['cols'] = self.supported_games[name].cols
+ param['aux'] = self.supported_games[name].aux
+ return param
+
+ def get_supported_games(self):
+ """Get the list of supported games."""
+ sg = []
+ for game in c.supported_games.keys():
+ sg.append(c.supported_games[game].game_name)
+ return sg
+
+if __name__== "__main__":
+ c = Config()
+
+ print "\n----------- SUPPORTED SITES -----------"
+ for s in c.supported_sites.keys():
+ print c.supported_sites[s]
+ print "----------- END SUPPORTED SITES -----------"
+
+
+ print "\n----------- SUPPORTED GAMES -----------"
+ for game in c.supported_games.keys():
+ print c.supported_games[game]
+ print "----------- END SUPPORTED GAMES -----------"
+
+
+ print "\n----------- SUPPORTED DATABASES -----------"
+ for db in c.supported_databases.keys():
+ print c.supported_databases[db]
+ print "----------- END SUPPORTED DATABASES -----------"
+
+ print "\n----------- AUX WINDOW FORMATS -----------"
+ for w in c.aux_windows.keys():
+ print c.aux_windows[w]
+ print "----------- END AUX WINDOW FORMATS -----------"
+
+ print "\n----------- POPUP WINDOW FORMATS -----------"
+ for w in c.popup_windows.keys():
+ print c.popup_windows[w]
+ print "----------- END POPUP WINDOW FORMATS -----------"
+
+ print "\n----------- IMPORT -----------"
+ print c.imp
+ 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.save(file="testout.xml")
+
+ print "db = ", c.get_db_parameters()
+# print "tv = ", c.get_tv_parameters()
+# print "imp = ", c.get_import_parameters()
+ print "paths = ", c.get_default_paths("PokerStars")
+ print "colors = ", c.get_default_colors("PokerStars")
+ print "locs = ", c.get_locations("PokerStars", 8)
+ for mw in c.get_aux_windows():
+ print c.get_aux_parameters(mw)
+
+ for site in c.supported_sites.keys():
+ print "site = ", site,
+ print c.get_site_parameters(site)
+
+ for game in c.get_supported_games():
+ print c.get_game_parameters(game)
diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py
index 4039b256..95abdd71 100644
--- a/pyfpdb/Database.py
+++ b/pyfpdb/Database.py
@@ -142,12 +142,27 @@ class Database:
cards[s_dict['seat_number']] = s_dict
return (cards)
- def get_stats_from_hand(self, hand, player_id = False):
+ def get_action_from_hand(self, hand_no):
+ action = [ [], [], [], [], [] ]
+ c = self.connection.cursor()
+ c.execute(self.sql.query['get_action_from_hand'], (hand_no))
+ for row in c.fetchall():
+ street = row[0]
+ act = row[1:]
+ action[street].append(act)
+ return action
+
+ def get_stats_from_hand(self, hand, aggregate = False):
c = self.connection.cursor()
- if not player_id: player_id = "%"
+ if aggregate:
+ query = 'get_stats_from_hand'
+ subs = (hand, hand)
+ else:
+ query = 'get_stats_from_hand_aggregated'
+ subs = (hand, hand, hand)
+
# get the players in the hand and their seats
-# c.execute(self.sql.query['get_players_from_hand'], (hand, player_id))
c.execute(self.sql.query['get_players_from_hand'], (hand, ))
names = {}
seats = {}
@@ -156,8 +171,7 @@ class Database:
seats[row[0]] = row[1]
# now get the stats
-# c.execute(self.sql.query['get_stats_from_hand'], (hand, hand, player_id))
- c.execute(self.sql.query['get_stats_from_hand'], (hand, hand))
+ c.execute(self.sql.query[query], subs)
colnames = [desc[0] for desc in c.description]
stat_dict = {}
for row in c.fetchall():
diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py
new file mode 100755
index 00000000..d60211b6
--- /dev/null
+++ b/pyfpdb/EverleafToFpdb.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python
+# Copyright 2008, Carl Gherardi
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+########################################################################
+
+import sys
+import Configuration
+from HandHistoryConverter import *
+
+# Everleaf HH format
+
+# Everleaf Gaming Game #55208539
+# ***** Hand history for game #55208539 *****
+# Blinds $0.50/$1 NL Hold'em - 2008/09/01 - 13:35:01
+# Table Speed Kuala
+# Seat 1 is the button
+# Total number of players: 9
+# Seat 1: BadBeatBox ( $ 98.97 USD )
+# Seat 3: EricBlade ( $ 73.96 USD )
+# Seat 4: randy888 ( $ 196.50 USD )
+# Seat 5: BaronSengir ( $ 182.80 USD )
+# Seat 6: dogge ( $ 186.06 USD )
+# Seat 7: wings ( $ 50 USD )
+# Seat 8: schoffeltje ( $ 282.05 USD )
+# Seat 9: harrydebeng ( $ 109.45 USD )
+# Seat 10: smaragdar ( $ 96.50 USD )
+# EricBlade: posts small blind [$ 0.50 USD]
+# randy888: posts big blind [$ 1 USD]
+# wings: posts big blind [$ 1 USD]
+# ** Dealing down cards **
+# Dealt to EricBlade [ qc, 3c ]
+# BaronSengir folds
+# dogge folds
+# wings raises [$ 2.50 USD]
+# schoffeltje folds
+# harrydebeng calls [$ 3.50 USD]
+# smaragdar raises [$ 15.50 USD]
+# BadBeatBox folds
+# EricBlade folds
+# randy888 folds
+# wings calls [$ 12 USD]
+# harrydebeng folds
+# ** Dealing Flop ** [ qs, 3d, 8h ]
+# wings: bets [$ 34.50 USD]
+# smaragdar calls [$ 34.50 USD]
+# ** Dealing Turn ** [ 2d ]
+# ** Dealing River ** [ 6c ]
+# dogge shows [ 9h, 9c ]a pair of nines
+# spicybum shows [ 5d, 6d ]a straight, eight high
+# harrydebeng does not show cards
+# smaragdar wins $ 102 USD from main pot with a pair of aces [ ad, ah, qs, 8h, 6c ]
+
+class Everleaf(HandHistoryConverter):
+ def __init__(self, config, file):
+ print "Initialising Everleaf converter class"
+ HandHistoryConverter.__init__(self, config, file, "Everleaf") # Call super class init.
+ self.sitename = "Everleaf"
+ self.setFileType("text")
+ self.rexx.setGameInfoRegex('.*Blinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+)')
+ self.rexx.setSplitHandRegex('\n\n\n\n')
+ self.rexx.setHandInfoRegex('.*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+) - (?P [0-9]+):(?P[0-9]+):(?P[0-9]+)\nTable (?P[ a-zA-Z]+)\nSeat (?P[0-9]+)')
+ self.rexx.setPlayerInfoRegex('Seat (?P[0-9]+): (?P.*) \( \$ (?P[.0-9]+) USD \)')
+ self.rexx.setPostSbRegex('.*\n(?P.*): posts small blind \[\$? (?P[.0-9]+)')
+ self.rexx.setPostBbRegex('.*\n(?P.*): posts big blind \[\$? (?P[.0-9]+)')
+ # mct : what about posting small & big blinds simultaneously?
+ self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P\S\S), (?P\S\S) \]')
+ self.rexx.setActionStepRegex('.*\n(?P.*) (?Pbets|checks|raises|calls|folds)(\s\[\$ (?P[.\d]+) USD\])?')
+ self.rexx.compileRegexes()
+
+ def readSupportedGames(self):
+ pass
+
+ def determineGameType(self):
+ # Cheating with this regex, only support nlhe at the moment
+ gametype = ["ring", "hold", "nl"]
+
+ m = self.rexx.game_info_re.search(self.obs)
+ gametype = gametype + [m.group('SB')]
+ gametype = gametype + [m.group('BB')]
+
+ return gametype
+
+ def readHandInfo(self, hand):
+ m = self.rexx.hand_info_re.search(hand.string)
+ hand.handid = m.group('HID')
+ hand.tablename = m.group('TABLE')
+# These work, but the info is already in the Hand class - should be used for tourneys though.
+# m.group('SB')
+# m.group('BB')
+# m.group('GAMETYPE')
+
+# Believe Everleaf time is GMT/UTC, no transation necessary
+# Stars format (Nov 10 2008): 2008/11/07 12:38:49 CET [2008/11/07 7:38:49 ET]
+# or : 2008/11/07 12:38:49 ET
+# Not getting it in my HH files yet, so using
+# 2008/11/10 3:58:52 ET
+#TODO: Do conversion from GMT to ET
+#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
+ hand.starttime = "%d/%02d/%02d %d:%02d:%02d ET" %(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')),
+ int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
+ hand.buttonpos = int(m.group('BUTTON'))
+
+ def readPlayerStacks(self, hand):
+ m = self.rexx.player_info_re.finditer(hand.string)
+ players = []
+
+ for a in m:
+ hand.addPlayer(a.group('SEAT'), a.group('PNAME'), a.group('CASH'))
+
+
+ def markStreets(self, hand):
+ # PREFLOP = ** Dealing down cards **
+ m = re.search('(\*\* Dealing down cards \*\*\n)(?P.*?\n\*\*)?( Dealing Flop \*\* \[ (?P\S\S), (?P\S\S), (?P\S\S) \])?(?P.*?\*\*)?( Dealing Turn \*\* \[ (?P\S\S) \])?(?P.*?\*\*)?( Dealing River \*\* \[ (?P\S\S) \])?(?P.*)', hand.string,re.DOTALL)
+# for street in m.groupdict():
+# print "DEBUG: Street: %s\tspan: %s" %(street, str(m.span(street)))
+ hand.streets = m
+
+ def readBlinds(self, hand):
+ try:
+ m = self.rexx.small_blind_re.search(hand.string)
+ hand.addBlind(m.group('PNAME'), m.group('SB'))
+ #hand.posted = [m.group('PNAME')]
+ except:
+ hand.addBlind(None, 0)
+ #hand.posted = ["FpdbNBP"]
+ m = self.rexx.big_blind_re.finditer(hand.string)
+ for a in m:
+ hand.addBlind(a.group('PNAME'), a.group('BB'))
+ #hand.posted = hand.posted + [a.group('PNAME')]
+
+ def readHeroCards(self, hand):
+ m = self.rexx.hero_cards_re.search(hand.string)
+ if(m == None):
+ #Not involved in hand
+ hand.involved = False
+ else:
+ hand.hero = m.group('PNAME')
+ hand.addHoleCards(m.group('HOLE1'), m.group('HOLE2'))
+
+ def readAction(self, hand, street):
+ m = self.rexx.action_re.finditer(hand.streets.group(street))
+ hand.actions[street] = []
+ for action in m:
+ 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') )
+ else:
+ print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)
+ hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]]
+
+
+ def getRake(self, hand):
+ hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right
+
+if __name__ == "__main__":
+ c = Configuration.Config()
+ e = Everleaf(c, "Speed_Kuala.txt")
+ e.processFile()
+ print str(e)
+
diff --git a/pyfpdb/FpdbRegex.py b/pyfpdb/FpdbRegex.py
new file mode 100644
index 00000000..8bfb9362
--- /dev/null
+++ b/pyfpdb/FpdbRegex.py
@@ -0,0 +1,161 @@
+# pokerstars_cash.py
+# -*- coding: iso-8859-15
+#
+# PokerStats, an online poker statistics tracking software for Linux
+# Copyright (C) 2007-2008 Mika Boström
+#
+# 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, version 3 of the License.
+#
+
+# Modified for use in fpdb by Carl Gherardi
+
+import re
+
+# These are PokerStars specific;
+# More importantly, they are currently valid for cash game only.
+#####
+# XXX: There was a weird problem with saved hand histories in PokerStars
+# client 2.491; if a user was present on the table (and thus anywhere in
+# the hand history), with non-standard characters in their username, the
+# client would prepend a literal Ctrl-P (ASCII 16, 0x10) character to
+# the hand history title line. Hence, to allow these strangely saved
+# hands to be parsed and imported, there is a conditional "one extra
+# character" allowed at the start of the new hand regex.
+
+
+class FpdbRegex:
+ def __init__(self):
+ self.__GAME_INFO_REGEX=''
+ self.__SPLIT_HAND_REGEX='\n\n\n'
+ self.__NEW_HAND_REGEX='^.?PokerStars Game #\d+:\s+Hold\'em'
+ self.__HAND_INFO_REGEX='^.*#(\d+):\s+(\S+)\s([\s\S]+)\s\(\$?([.0-9]+)/\$?([.0-9]+)\)\s-\s(\S+)\s-?\s?(\S+)\s\(?(\w+)\)?'
+ self.__TABLE_INFO_REGEX='^\S+\s+\'.*\'\s+(\d+)-max\s+Seat\s#(\d+)'
+ self.__PLAYER_INFO_REGEX='^Seat\s(\d+):\s(.*)\s\(\$?([.\d]+)\s'
+ self.__POST_SB_REGEX='^(.*):\sposts small blind'
+ self.__POST_BB_REGEX='^(.*):\sposts big blind'
+ self.__POST_BOTH_REGEX='^(.*):\sposts small & big blinds'
+ self.__HAND_STAGE_REGEX='^\*{3}\s(.*)\s\*{3}'
+ self.__HOLE_CARD_REGEX='^\*{3}\sHOLE CARDS'
+ self.__FLOP_CARD_REGEX='^\*{3}\sFLOP\s\*{3}\s\[(\S{2})\s(\S{2})\s(\S{2})\]'
+ self.__TURN_CARD_REGEX='^\*{3}\sTURN\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
+ self.__RIVER_CARD_REGEX='^\*{3}\sRIVER\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
+ self.__SHOWDOWN_REGEX='^\*{3}\sSHOW DOWN'
+ self.__SUMMARY_REGEX='^\*{3}\sSUMMARY'
+ self.__UNCALLED_BET_REGEX='^Uncalled bet \(\$([.\d]+)\) returned to (.*)'
+ self.__POT_AND_RAKE_REGEX='^Total\spot\s\$([.\d]+).*\|\sRake\s\$([.\d]+)'
+ self.__COLLECT_POT_REGEX='^(.*)\scollected\s\$([.\d]+)\sfrom\s((main|side)\s)?pot'
+ self.__HERO_CARDS_REGEX='^Dealt\sto\s(.*)\s\[(\S{2})\s(\S{2})\]'
+ self.__SHOWN_CARDS_REGEX='^(.*):\sshows\s\[(\S{2})\s(\S{2})\]'
+ self.__ACTION_STEP_REGEX='^(.*):\s(bets|checks|raises|calls|folds)((\s\$([.\d]+))?(\sto\s\$([.\d]+))?)?'
+
+ self.__SHOWDOWN_ACTION_REGEX='^(.*):\s(shows|mucks)'
+ self.__SUMMARY_CARDS_REGEX='^Seat\s\d+:\s(.*)\s(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
+ self.__SUMMARY_CARDS_EXTRA_REGEX='^Seat\s\d+:\s(.*)\s(\(.*\)\s)(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
+
+ def compileRegexes(self):
+ ### Compile the regexes
+ self.game_info_re = re.compile(self.__GAME_INFO_REGEX)
+ self.split_hand_re = re.compile(self.__SPLIT_HAND_REGEX)
+ self.hand_start_re = re.compile(self.__NEW_HAND_REGEX)
+ self.hand_info_re = re.compile(self.__HAND_INFO_REGEX)
+ self.table_info_re = re.compile(self.__TABLE_INFO_REGEX)
+ self.player_info_re = re.compile(self.__PLAYER_INFO_REGEX)
+ self.small_blind_re = re.compile(self.__POST_SB_REGEX)
+ self.big_blind_re = re.compile(self.__POST_BB_REGEX)
+ self.both_blinds_re = re.compile(self.__POST_BOTH_REGEX)
+ self.hand_stage_re = re.compile(self.__HAND_STAGE_REGEX)
+ self.hole_cards_re = re.compile(self.__HOLE_CARD_REGEX)
+ self.flop_cards_re = re.compile(self.__FLOP_CARD_REGEX)
+ self.turn_card_re = re.compile(self.__TURN_CARD_REGEX)
+ self.river_card_re = re.compile(self.__RIVER_CARD_REGEX)
+ self.showdown_re = re.compile(self.__SHOWDOWN_REGEX)
+ self.summary_re = re.compile(self.__SUMMARY_REGEX)
+ self.uncalled_bet_re = re.compile(self.__UNCALLED_BET_REGEX)
+ self.collect_pot_re = re.compile(self.__COLLECT_POT_REGEX)
+ self.hero_cards_re = re.compile(self.__HERO_CARDS_REGEX)
+ self.cards_shown_re = re.compile(self.__SHOWN_CARDS_REGEX)
+ self.summary_cards_re = re.compile(self.__SUMMARY_CARDS_REGEX)
+ self.summary_cards_extra_re = re.compile(self.__SUMMARY_CARDS_EXTRA_REGEX)
+ self.action_re = re.compile(self.__ACTION_STEP_REGEX)
+ self.rake_re = re.compile(self.__POT_AND_RAKE_REGEX)
+ self.showdown_action_re = re.compile(self.__SHOWDOWN_ACTION_REGEX)
+
+ # Set methods for plugins to override
+
+ def setGameInfoRegex(self, string):
+ self.__GAME_INFO_REGEX = string
+
+ def setSplitHandRegex(self, string):
+ self.__SPLIT_HAND_REGEX = string
+
+ def setNewHandRegex(self, string):
+ self.__NEW_HAND_REGEX = string
+
+ def setHandInfoRegex(self, string):
+ self.__HAND_INFO_REGEX = string
+
+ def setTableInfoRegex(self, string):
+ self.__TABLE_INFO_REGEX = string
+
+ def setPlayerInfoRegex(self, string):
+ self.__PLAYER_INFO_REGEX = string
+
+ def setPostSbRegex(self, string):
+ self.__POST_SB_REGEX = string
+
+ def setPostBbRegex(self, string):
+ self.__POST_BB_REGEX = string
+
+ def setPostBothRegex(self, string):
+ self.__POST_BOTH_REGEX = string
+
+ def setHandStageRegex(self, string):
+ self.__HAND_STAGE_REGEX = string
+
+ def setHoleCardRegex(self, string):
+ self.__HOLE_CARD_REGEX = string
+
+ def setFlopCardRegex(self, string):
+ self.__FLOP_CARD_REGEX = string
+
+ def setTurnCardRegex(self, string):
+ self.__TURN_CARD_REGEX = string
+
+ def setRiverCardRegex(self, string):
+ self.__RIVER_CARD_REGEX = string
+
+ def setShowdownRegex(self, string):
+ self.__SHOWDOWN_REGEX = string
+
+ def setSummaryRegex(self, string):
+ self.__SUMMARY_REGEX = string
+
+ def setUncalledBetRegex(self, string):
+ self.__UNCALLED_BET_REGEX = string
+
+ def setCollectPotRegex(self, string):
+ self.__COLLECT_POT_REGEX = string
+
+ def setHeroCardsRegex(self, string):
+ self.__HERO_CARDS_REGEX = string
+
+ def setShownCardsRegex(self, string):
+ self.__SHOWN_CARDS_REGEX = string
+
+ def setSummaryCardsRegex(self, string):
+ self.__SUMMARY_CARDS_REGEX = string
+
+ def setSummaryCardsExtraRegex(self, string):
+ self.__SUMMARY_CARDS_EXTRA_REGEX = string
+
+ def setActionStepRegex(self, string):
+ self.__ACTION_STEP_REGEX = string
+
+ def setPotAndRakeRegex(self, string):
+ self.__POT_AND_RAKE_REGEX = string
+
+ def setShowdownActionRegex(self, string):
+ self.__SHOWDOWN_ACTION_REGEX = string
+
diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py
index b3dbf8c7..a3d918ac 100644
--- a/pyfpdb/FpdbSQLQueries.py
+++ b/pyfpdb/FpdbSQLQueries.py
@@ -30,666 +30,636 @@ class FpdbSQLQueries:
self.query = {}
self.dbname = db
- ################################
- # List tables
- ################################
+ ################################
+ # List tables
+ ################################
if(self.dbname == 'MySQL InnoDB'):
- self.query['list_tables'] = """SHOW TABLES"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['list_tables'] = """SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"""
- elif(self.dbname == 'SQLite'):
- self.query['list_tables'] = """ """
+ self.query['list_tables'] = """SHOW TABLES"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['list_tables'] = """SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"""
+ elif(self.dbname == 'SQLite'):
+ self.query['list_tables'] = """ """
- ##################################################################
+ ##################################################################
# Drop Tables - MySQL, PostgreSQL and SQLite all share same syntax
##################################################################
- if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'):
- self.query['drop_table'] = """DROP TABLE IF EXISTS """
+ if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'):
+ self.query['drop_table'] = """DROP TABLE IF EXISTS """
- ################################
- # Create Tables
- ################################
+ ################################
+ # Create Tables
+ ################################
- ################################
- # Create Settings
- ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createSettingsTable'] = """CREATE TABLE Settings (
- version SMALLINT NOT NULL)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createSettingsTable'] = """CREATE TABLE Settings (version SMALLINT)"""
+ ################################
+ # Create Settings
+ ################################
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createSettingsTable'] = """CREATE TABLE Settings (
+ version SMALLINT NOT NULL)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createSettingsTable'] = """CREATE TABLE Settings (version SMALLINT)"""
- elif(self.dbname == 'SQLite'):
+ elif(self.dbname == 'SQLite'):
#Probably doesn't work.
- self.query['createSettingsTable'] = """ """
+ self.query['createSettingsTable'] = """ """
- ################################
- # Create Sites
- ################################
+ ################################
+ # Create Sites
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createSitesTable'] = """CREATE TABLE Sites (
- id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- name varchar(32) NOT NULL,
- currency char(3) NOT NULL)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createSitesTable'] = """CREATE TABLE Sites (
- id SERIAL, PRIMARY KEY (id),
- name varchar(32),
- currency char(3))"""
- elif(self.dbname == 'SQLite'):
- self.query['createSitesTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createSitesTable'] = """CREATE TABLE Sites (
+ id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ name varchar(32) NOT NULL,
+ currency char(3) NOT NULL)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createSitesTable'] = """CREATE TABLE Sites (
+ id SERIAL UNIQUE, PRIMARY KEY (id),
+ name varchar(32),
+ currency char(3))"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createSitesTable'] = """ """
- ################################
- # Create Gametypes
- ################################
+ ################################
+ # Create Gametypes
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
- id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
- type char(4) NOT NULL,
- base char(4) NOT NULL,
- category varchar(9) NOT NULL,
- limitType char(2) NOT NULL,
- hiLo char(1) NOT NULL,
- smallBlind int,
- bigBlind int,
- smallBet int NOT NULL,
- bigBet int NOT NULL)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
- id SERIAL, PRIMARY KEY (id),
- siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
- type char(4),
- base char(4),
- category varchar(9),
- limitType char(2),
- hiLo char(1),
- smallBlind int,
- bigBlind int,
- smallBet int,
- bigBet int)"""
- elif(self.dbname == 'SQLite'):
- self.query['createGametypesTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
+ id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
+ type char(4) NOT NULL,
+ base char(4) NOT NULL,
+ category varchar(9) NOT NULL,
+ limitType char(2) NOT NULL,
+ hiLo char(1) NOT NULL,
+ smallBlind int,
+ bigBlind int,
+ smallBet int NOT NULL,
+ bigBet int NOT NULL)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
+ id SERIAL UNIQUE, PRIMARY KEY (id),
+ siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
+ type char(4),
+ base char(4),
+ category varchar(9),
+ limitType char(2),
+ hiLo char(1),
+ smallBlind int,
+ bigBlind int,
+ smallBet int,
+ bigBet int)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createGametypesTable'] = """ """
- ################################
- # Create Players
- ################################
+ ################################
+ # Create Players
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createPlayersTable'] = """CREATE TABLE Players (
- id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- name VARCHAR(32) CHARACTER SET utf8 NOT NULL,
- siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
- comment text,
- commentTs DATETIME)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createPlayersTable'] = """CREATE TABLE Players (
- id SERIAL, PRIMARY KEY (id),
- name VARCHAR(32),
- siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
- comment text,
- commentTs timestamp without time zone)"""
- elif(self.dbname == 'SQLite'):
- self.query['createPlayersTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createPlayersTable'] = """CREATE TABLE Players (
+ id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ name VARCHAR(32) CHARACTER SET utf8 NOT NULL,
+ siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
+ comment text,
+ commentTs DATETIME)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createPlayersTable'] = """CREATE TABLE Players (
+ id SERIAL UNIQUE, PRIMARY KEY (id),
+ name VARCHAR(32),
+ siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
+ comment text,
+ commentTs timestamp without time zone)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createPlayersTable'] = """ """
- ################################
- # Create Autorates
- ################################
+ ################################
+ # Create Autorates
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
- id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
- gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
- description varchar(50) NOT NULL,
- shortDesc char(8) NOT NULL,
- ratingTime DATETIME NOT NULL,
- handCount int NOT NULL)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
- id BIGSERIAL, PRIMARY KEY (id),
- playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
- gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
- description varchar(50),
- shortDesc char(8),
- ratingTime timestamp without time zone,
- handCount int)"""
- elif(self.dbname == 'SQLite'):
- self.query['createAutoratesTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
+ id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
+ gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
+ description varchar(50) NOT NULL,
+ shortDesc char(8) NOT NULL,
+ ratingTime DATETIME NOT NULL,
+ handCount int NOT NULL)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
+ id BIGSERIAL UNIQUE, PRIMARY KEY (id),
+ playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
+ gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
+ description varchar(50),
+ shortDesc char(8),
+ ratingTime timestamp without time zone,
+ handCount int)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createAutoratesTable'] = """ """
- ################################
- # Create Hands
- ################################
+ ################################
+ # Create Hands
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createHandsTable'] = """CREATE TABLE Hands (
- id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- tableName VARCHAR(20) NOT NULL,
- siteHandNo BIGINT NOT NULL,
- gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
- handStart DATETIME NOT NULL,
- importTime DATETIME NOT NULL,
- seats SMALLINT NOT NULL,
- maxSeats SMALLINT NOT NULL,
- comment TEXT,
- commentTs DATETIME)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createHandsTable'] = """CREATE TABLE Hands (
- id BIGSERIAL, PRIMARY KEY (id),
- tableName VARCHAR(20),
- siteHandNo BIGINT,
- gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
- handStart timestamp without time zone,
- importTime timestamp without time zone,
- seats SMALLINT,
- maxSeats SMALLINT,
- comment TEXT,
- commentTs timestamp without time zone)"""
- elif(self.dbname == 'SQLite'):
- self.query['createHandsTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createHandsTable'] = """CREATE TABLE Hands (
+ id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ tableName VARCHAR(20) NOT NULL,
+ siteHandNo BIGINT NOT NULL,
+ gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
+ handStart DATETIME NOT NULL,
+ importTime DATETIME NOT NULL,
+ seats SMALLINT NOT NULL,
+ maxSeats SMALLINT NOT NULL,
+ comment TEXT,
+ commentTs DATETIME)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createHandsTable'] = """CREATE TABLE Hands (
+ id BIGSERIAL UNIQUE, PRIMARY KEY (id),
+ tableName VARCHAR(20),
+ siteHandNo BIGINT,
+ gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
+ handStart timestamp without time zone,
+ importTime timestamp without time zone,
+ seats SMALLINT,
+ maxSeats SMALLINT,
+ comment TEXT,
+ commentTs timestamp without time zone)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createHandsTable'] = """ """
- ################################
- # Create Gametypes
- ################################
+ ################################
+ # Create Gametypes
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
- id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
- card1Value smallint NOT NULL,
- card1Suit char(1) NOT NULL,
- card2Value smallint NOT NULL,
- card2Suit char(1) NOT NULL,
- card3Value smallint NOT NULL,
- card3Suit char(1) NOT NULL,
- card4Value smallint NOT NULL,
- card4Suit char(1) NOT NULL,
- card5Value smallint NOT NULL,
- card5Suit char(1) NOT NULL)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
- id BIGSERIAL, PRIMARY KEY (id),
- handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
- card1Value smallint,
- card1Suit char(1),
- card2Value smallint,
- card2Suit char(1),
- card3Value smallint,
- card3Suit char(1),
- card4Value smallint,
- card4Suit char(1),
- card5Value smallint,
- card5Suit char(1))"""
- elif(self.dbname == 'SQLite'):
- self.query['createBoardCardsTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
+ id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
+ card1Value smallint NOT NULL,
+ card1Suit char(1) NOT NULL,
+ card2Value smallint NOT NULL,
+ card2Suit char(1) NOT NULL,
+ card3Value smallint NOT NULL,
+ card3Suit char(1) NOT NULL,
+ card4Value smallint NOT NULL,
+ card4Suit char(1) NOT NULL,
+ card5Value smallint NOT NULL,
+ card5Suit char(1) NOT NULL)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
+ id BIGSERIAL UNIQUE, PRIMARY KEY (id),
+ handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
+ card1Value smallint,
+ card1Suit char(1),
+ card2Value smallint,
+ card2Suit char(1),
+ card3Value smallint,
+ card3Suit char(1),
+ card4Value smallint,
+ card4Suit char(1),
+ card5Value smallint,
+ card5Suit char(1))"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createBoardCardsTable'] = """ """
- ################################
- # Create TourneyTypes
- ################################
+ ################################
+ # Create TourneyTypes
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
- id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
- buyin INT NOT NULL,
- fee INT NOT NULL,
- knockout INT NOT NULL,
- rebuyOrAddon BOOLEAN NOT NULL)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
- id SERIAL, PRIMARY KEY (id),
- siteId INT, FOREIGN KEY (siteId) REFERENCES Sites(id),
- buyin INT,
- fee INT,
- knockout INT,
- rebuyOrAddon BOOLEAN)"""
- elif(self.dbname == 'SQLite'):
- self.query['createTourneyTypesTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
+ id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
+ buyin INT NOT NULL,
+ fee INT NOT NULL,
+ knockout INT NOT NULL,
+ rebuyOrAddon BOOLEAN NOT NULL)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
+ id SERIAL, PRIMARY KEY (id),
+ siteId INT, FOREIGN KEY (siteId) REFERENCES Sites(id),
+ buyin INT,
+ fee INT,
+ knockout INT,
+ rebuyOrAddon BOOLEAN)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createTourneyTypesTable'] = """ """
- ################################
- # Create Tourneys
- ################################
+ ################################
+ # Create Tourneys
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
- id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
- siteTourneyNo BIGINT NOT NULL,
- entries INT NOT NULL,
- prizepool INT NOT NULL,
- startTime DATETIME NOT NULL,
- comment TEXT,
- commentTs DATETIME)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
- id SERIAL, PRIMARY KEY (id),
- tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
- siteTourneyNo BIGINT,
- entries INT,
- prizepool INT,
- startTime timestamp without time zone,
- comment TEXT,
- commentTs timestamp without time zone)"""
- elif(self.dbname == 'SQLite'):
- self.query['createTourneysTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
+ id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
+ siteTourneyNo BIGINT NOT NULL,
+ entries INT NOT NULL,
+ prizepool INT NOT NULL,
+ startTime DATETIME NOT NULL,
+ comment TEXT,
+ commentTs DATETIME)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
+ id SERIAL UNIQUE, PRIMARY KEY (id),
+ tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
+ siteTourneyNo BIGINT,
+ entries INT,
+ prizepool INT,
+ startTime timestamp without time zone,
+ comment TEXT,
+ commentTs timestamp without time zone)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createTourneysTable'] = """ """
- ################################
- # Create HandsPlayers
- ################################
+ ################################
+ # Create HandsPlayers
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
- id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
- playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
- startCash INT NOT NULL,
- position CHAR(1),
- seatNo SMALLINT NOT NULL,
- ante INT,
-
- card1Value smallint NOT NULL,
- card1Suit char(1) NOT NULL,
- card2Value smallint NOT NULL,
- card2Suit char(1) NOT NULL,
- card3Value smallint,
- card3Suit char(1),
- card4Value smallint,
- card4Suit char(1),
- card5Value smallint,
- card5Suit char(1),
- card6Value smallint,
- card6Suit char(1),
- card7Value smallint,
- card7Suit char(1),
-
- winnings int NOT NULL,
- rake int NOT NULL,
- comment text,
- commentTs DATETIME,
-
- tourneysPlayersId BIGINT UNSIGNED, FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id))
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
- id BIGSERIAL, PRIMARY KEY (id),
- handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
- playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
- startCash INT,
- position CHAR(1),
- seatNo SMALLINT,
- ante INT,
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
+ id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
+ playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
+ startCash INT NOT NULL,
+ position CHAR(1),
+ seatNo SMALLINT NOT NULL,
+ ante INT,
+
+ card1Value smallint NOT NULL,
+ card1Suit char(1) NOT NULL,
+ card2Value smallint NOT NULL,
+ card2Suit char(1) NOT NULL,
+ card3Value smallint,
+ card3Suit char(1),
+ card4Value smallint,
+ card4Suit char(1),
+ card5Value smallint,
+ card5Suit char(1),
+ card6Value smallint,
+ card6Suit char(1),
+ card7Value smallint,
+ card7Suit char(1),
+
+ winnings int NOT NULL,
+ rake int NOT NULL,
+ comment text,
+ commentTs DATETIME,
+
+ tourneysPlayersId BIGINT UNSIGNED, FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id))
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
+ id BIGSERIAL UNIQUE, PRIMARY KEY (id),
+ handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
+ playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
+ startCash INT,
+ position CHAR(1),
+ seatNo SMALLINT,
+ ante INT,
- card1Value smallint,
- card1Suit char(1),
- card2Value smallint,
- card2Suit char(1),
- card3Value smallint,
- card3Suit char(1),
- card4Value smallint,
- card4Suit char(1),
- card5Value smallint,
- card5Suit char(1),
- card6Value smallint,
- card6Suit char(1),
- card7Value smallint,
- card7Suit char(1),
+ card1Value smallint,
+ card1Suit char(1),
+ card2Value smallint,
+ card2Suit char(1),
+ card3Value smallint,
+ card3Suit char(1),
+ card4Value smallint,
+ card4Suit char(1),
+ card5Value smallint,
+ card5Suit char(1),
+ card6Value smallint,
+ card6Suit char(1),
+ card7Value smallint,
+ card7Suit char(1),
- winnings int,
- rake int,
- comment text,
- commentTs timestamp without time zone,
- tourneysPlayersId BIGINT, FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id))"""
- elif(self.dbname == 'SQLite'):
- self.query['createHandsPlayersTable'] = """ """
+ winnings int,
+ rake int,
+ comment text,
+ commentTs timestamp without time zone,
+ tourneysPlayersId BIGINT, FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id))"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createHandsPlayersTable'] = """ """
- ################################
- # Create TourneysPlayers
- ################################
+ ################################
+ # Create TourneysPlayers
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
- id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- tourneyId INT UNSIGNED NOT NULL, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
- playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
- payinAmount INT NOT NULL,
- rank INT NOT NULL,
- winnings INT NOT NULL,
- comment TEXT,
- commentTs DATETIME)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
- id BIGSERIAL, PRIMARY KEY (id),
- tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
- playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
- payinAmount INT,
- rank INT,
- winnings INT,
- comment TEXT,
- commentTs timestamp without time zone)"""
- elif(self.dbname == 'SQLite'):
- self.query['createTourneysPlayersTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
+ id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ tourneyId INT UNSIGNED NOT NULL, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
+ playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
+ payinAmount INT NOT NULL,
+ rank INT NOT NULL,
+ winnings INT NOT NULL,
+ comment TEXT,
+ commentTs DATETIME)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
+ id BIGSERIAL UNIQUE, PRIMARY KEY (id),
+ tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
+ playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
+ payinAmount INT,
+ rank INT,
+ winnings INT,
+ comment TEXT,
+ commentTs timestamp without time zone)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createTourneysPlayersTable'] = """ """
- ################################
- # Create HandsActions
- ################################
+ ################################
+ # Create HandsActions
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
- id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- handPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
- street SMALLINT NOT NULL,
- actionNo SMALLINT NOT NULL,
- action CHAR(5) NOT NULL,
- allIn BOOLEAN NOT NULL,
- amount INT NOT NULL,
- comment TEXT,
- commentTs DATETIME)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
- id BIGSERIAL, PRIMARY KEY (id),
- handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
- street SMALLINT,
- actionNo SMALLINT,
- action CHAR(5),
- allIn BOOLEAN,
- amount INT,
- comment TEXT,
- commentTs timestamp without time zone)"""
- elif(self.dbname == 'SQLite'):
- self.query['createHandsActionsTable'] = """ """
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
+ id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ handPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
+ street SMALLINT NOT NULL,
+ actionNo SMALLINT NOT NULL,
+ action CHAR(5) NOT NULL,
+ allIn BOOLEAN NOT NULL,
+ amount INT NOT NULL,
+ comment TEXT,
+ commentTs DATETIME)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
+ id BIGSERIAL UNIQUE, PRIMARY KEY (id),
+ handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
+ street SMALLINT,
+ actionNo SMALLINT,
+ action CHAR(5),
+ allIn BOOLEAN,
+ amount INT,
+ comment TEXT,
+ commentTs timestamp without time zone)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createHandsActionsTable'] = """ """
- ################################
- # Create HudCache
- ################################
+ ################################
+ # Create HudCache
+ ################################
- if(self.dbname == 'MySQL InnoDB'):
- self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
- id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
- gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
- playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
- activeSeats SMALLINT NOT NULL,
- position CHAR(1),
- tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
-
- HDs INT NOT NULL,
- street0VPI INT NOT NULL,
- street0Aggr INT NOT NULL,
- street0_3B4BChance INT NOT NULL,
- street0_3B4BDone INT NOT NULL,
-
- street1Seen INT NOT NULL,
- street2Seen INT NOT NULL,
- street3Seen INT NOT NULL,
- street4Seen INT NOT NULL,
- sawShowdown INT NOT NULL,
-
- street1Aggr INT NOT NULL,
- street2Aggr INT NOT NULL,
- street3Aggr INT NOT NULL,
- street4Aggr INT NOT NULL,
-
- otherRaisedStreet1 INT NOT NULL,
- otherRaisedStreet2 INT NOT NULL,
- otherRaisedStreet3 INT NOT NULL,
- otherRaisedStreet4 INT NOT NULL,
- foldToOtherRaisedStreet1 INT NOT NULL,
- foldToOtherRaisedStreet2 INT NOT NULL,
- foldToOtherRaisedStreet3 INT NOT NULL,
- foldToOtherRaisedStreet4 INT NOT NULL,
- wonWhenSeenStreet1 FLOAT NOT NULL,
- wonAtSD FLOAT NOT NULL,
-
- stealAttemptChance INT NOT NULL,
- stealAttempted INT NOT NULL,
- foldBbToStealChance INT NOT NULL,
- foldedBbToSteal INT NOT NULL,
- foldSbToStealChance INT NOT NULL,
- foldedSbToSteal INT NOT NULL,
-
- street1CBChance INT NOT NULL,
- street1CBDone INT NOT NULL,
- street2CBChance INT NOT NULL,
- street2CBDone INT NOT NULL,
- street3CBChance INT NOT NULL,
- street3CBDone INT NOT NULL,
- street4CBChance INT NOT NULL,
- street4CBDone INT NOT NULL,
-
- foldToStreet1CBChance INT NOT NULL,
- foldToStreet1CBDone INT NOT NULL,
- foldToStreet2CBChance INT NOT NULL,
- foldToStreet2CBDone INT NOT NULL,
- foldToStreet3CBChance INT NOT NULL,
- foldToStreet3CBDone INT NOT NULL,
- foldToStreet4CBChance INT NOT NULL,
- foldToStreet4CBDone INT NOT NULL,
-
- totalProfit INT NOT NULL,
-
- street1CheckCallRaiseChance INT NOT NULL,
- street1CheckCallRaiseDone INT NOT NULL,
- street2CheckCallRaiseChance INT NOT NULL,
- street2CheckCallRaiseDone INT NOT NULL,
- street3CheckCallRaiseChance INT NOT NULL,
- street3CheckCallRaiseDone INT NOT NULL,
- street4CheckCallRaiseChance INT NOT NULL,
- street4CheckCallRaiseDone INT NOT NULL)
- ENGINE=INNODB"""
- elif(self.dbname == 'PostgreSQL'):
- self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
- id BIGSERIAL, PRIMARY KEY (id),
- gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
- playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
- activeSeats SMALLINT,
- position CHAR(1),
- tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
+ if(self.dbname == 'MySQL InnoDB'):
+ self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
+ id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
+ gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
+ playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
+ activeSeats SMALLINT NOT NULL,
+ position CHAR(1),
+ tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
+
+ HDs INT NOT NULL,
+ street0VPI INT NOT NULL,
+ street0Aggr INT NOT NULL,
+ street0_3B4BChance INT NOT NULL,
+ street0_3B4BDone INT NOT NULL,
+
+ street1Seen INT NOT NULL,
+ street2Seen INT NOT NULL,
+ street3Seen INT NOT NULL,
+ street4Seen INT NOT NULL,
+ sawShowdown INT NOT NULL,
+
+ street1Aggr INT NOT NULL,
+ street2Aggr INT NOT NULL,
+ street3Aggr INT NOT NULL,
+ street4Aggr INT NOT NULL,
+
+ otherRaisedStreet1 INT NOT NULL,
+ otherRaisedStreet2 INT NOT NULL,
+ otherRaisedStreet3 INT NOT NULL,
+ otherRaisedStreet4 INT NOT NULL,
+ foldToOtherRaisedStreet1 INT NOT NULL,
+ foldToOtherRaisedStreet2 INT NOT NULL,
+ foldToOtherRaisedStreet3 INT NOT NULL,
+ foldToOtherRaisedStreet4 INT NOT NULL,
+ wonWhenSeenStreet1 FLOAT NOT NULL,
+ wonAtSD FLOAT NOT NULL,
+
+ stealAttemptChance INT NOT NULL,
+ stealAttempted INT NOT NULL,
+ foldBbToStealChance INT NOT NULL,
+ foldedBbToSteal INT NOT NULL,
+ foldSbToStealChance INT NOT NULL,
+ foldedSbToSteal INT NOT NULL,
+
+ street1CBChance INT NOT NULL,
+ street1CBDone INT NOT NULL,
+ street2CBChance INT NOT NULL,
+ street2CBDone INT NOT NULL,
+ street3CBChance INT NOT NULL,
+ street3CBDone INT NOT NULL,
+ street4CBChance INT NOT NULL,
+ street4CBDone INT NOT NULL,
+
+ foldToStreet1CBChance INT NOT NULL,
+ foldToStreet1CBDone INT NOT NULL,
+ foldToStreet2CBChance INT NOT NULL,
+ foldToStreet2CBDone INT NOT NULL,
+ foldToStreet3CBChance INT NOT NULL,
+ foldToStreet3CBDone INT NOT NULL,
+ foldToStreet4CBChance INT NOT NULL,
+ foldToStreet4CBDone INT NOT NULL,
+
+ totalProfit INT NOT NULL,
+
+ street1CheckCallRaiseChance INT NOT NULL,
+ street1CheckCallRaiseDone INT NOT NULL,
+ street2CheckCallRaiseChance INT NOT NULL,
+ street2CheckCallRaiseDone INT NOT NULL,
+ street3CheckCallRaiseChance INT NOT NULL,
+ street3CheckCallRaiseDone INT NOT NULL,
+ street4CheckCallRaiseChance INT NOT NULL,
+ street4CheckCallRaiseDone INT NOT NULL)
+ ENGINE=INNODB"""
+ elif(self.dbname == 'PostgreSQL'):
+ self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
+ id BIGSERIAL UNIQUE, PRIMARY KEY (id),
+ gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
+ playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
+ activeSeats SMALLINT,
+ position CHAR(1),
+ tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
- HDs INT,
- street0VPI INT,
- street0Aggr INT,
- street0_3B4BChance INT,
- street0_3B4BDone INT,
- street1Seen INT,
- street2Seen INT,
- street3Seen INT,
- street4Seen INT,
- sawShowdown INT,
- street1Aggr INT,
- street2Aggr INT,
- street3Aggr INT,
- street4Aggr INT,
- otherRaisedStreet1 INT,
- otherRaisedStreet2 INT,
- otherRaisedStreet3 INT,
- otherRaisedStreet4 INT,
- foldToOtherRaisedStreet1 INT,
- foldToOtherRaisedStreet2 INT,
- foldToOtherRaisedStreet3 INT,
- foldToOtherRaisedStreet4 INT,
- wonWhenSeenStreet1 FLOAT,
- wonAtSD FLOAT,
+ HDs INT,
+ street0VPI INT,
+ street0Aggr INT,
+ street0_3B4BChance INT,
+ street0_3B4BDone INT,
+ street1Seen INT,
+ street2Seen INT,
+ street3Seen INT,
+ street4Seen INT,
+ sawShowdown INT,
+ street1Aggr INT,
+ street2Aggr INT,
+ street3Aggr INT,
+ street4Aggr INT,
+ otherRaisedStreet1 INT,
+ otherRaisedStreet2 INT,
+ otherRaisedStreet3 INT,
+ otherRaisedStreet4 INT,
+ foldToOtherRaisedStreet1 INT,
+ foldToOtherRaisedStreet2 INT,
+ foldToOtherRaisedStreet3 INT,
+ foldToOtherRaisedStreet4 INT,
+ wonWhenSeenStreet1 FLOAT,
+ wonAtSD FLOAT,
- stealAttemptChance INT,
- stealAttempted INT,
- foldBbToStealChance INT,
- foldedBbToSteal INT,
- foldSbToStealChance INT,
- foldedSbToSteal INT,
+ stealAttemptChance INT,
+ stealAttempted INT,
+ foldBbToStealChance INT,
+ foldedBbToSteal INT,
+ foldSbToStealChance INT,
+ foldedSbToSteal INT,
- street1CBChance INT,
- street1CBDone INT,
- street2CBChance INT,
- street2CBDone INT,
- street3CBChance INT,
- street3CBDone INT,
- street4CBChance INT,
- street4CBDone INT,
+ street1CBChance INT,
+ street1CBDone INT,
+ street2CBChance INT,
+ street2CBDone INT,
+ street3CBChance INT,
+ street3CBDone INT,
+ street4CBChance INT,
+ street4CBDone INT,
- foldToStreet1CBChance INT,
- foldToStreet1CBDone INT,
- foldToStreet2CBChance INT,
- foldToStreet2CBDone INT,
- foldToStreet3CBChance INT,
- foldToStreet3CBDone INT,
- foldToStreet4CBChance INT,
- foldToStreet4CBDone INT,
+ foldToStreet1CBChance INT,
+ foldToStreet1CBDone INT,
+ foldToStreet2CBChance INT,
+ foldToStreet2CBDone INT,
+ foldToStreet3CBChance INT,
+ foldToStreet3CBDone INT,
+ foldToStreet4CBChance INT,
+ foldToStreet4CBDone INT,
- totalProfit INT,
+ totalProfit INT,
- street1CheckCallRaiseChance INT,
- street1CheckCallRaiseDone INT,
- street2CheckCallRaiseChance INT,
- street2CheckCallRaiseDone INT,
- street3CheckCallRaiseChance INT,
- street3CheckCallRaiseDone INT,
- street4CheckCallRaiseChance INT,
- street4CheckCallRaiseDone INT)"""
- elif(self.dbname == 'SQLite'):
- self.query['createHudCacheTable'] = """ """
+ street1CheckCallRaiseChance INT,
+ street1CheckCallRaiseDone INT,
+ street2CheckCallRaiseChance INT,
+ street2CheckCallRaiseDone INT,
+ street3CheckCallRaiseChance INT,
+ street3CheckCallRaiseDone INT,
+ street4CheckCallRaiseChance INT,
+ street4CheckCallRaiseDone INT)"""
+ elif(self.dbname == 'SQLite'):
+ self.query['createHudCacheTable'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
elif(self.dbname == 'PostgreSQL'):
- # FIXME: This query has a different syntax
- self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
+ self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)"""
elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
elif(self.dbname == 'PostgreSQL'):
- # FIXME: This query has a different syntax
- self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
+ self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)"""
elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)"""
elif(self.dbname == 'PostgreSQL'):
- # FIXME: This query has a different syntax
- self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
+ self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)"""
elif(self.dbname == 'SQLite'):
- self.query['addHandsIndex'] = """ """
+ self.query['addPlayersIndex'] = """ """
- ################################
- # Queries used in GuiGraphViewer
- ################################
+ ################################
+ # Queries used in GuiGraphViewer
+ ################################
- # Returns all cash game handIds and the money won(winnings is the final pot)
- # by the playerId for a single site.
- if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
- self.query['getRingWinningsAllGamesPlayerIdSite'] = """SELECT handId, winnings FROM HandsPlayers
- INNER JOIN Players ON HandsPlayers.playerId = Players.id
- INNER JOIN Hands ON Hands.id = HandsPlayers.handId
- WHERE Players.name = %s AND Players.siteId = %s AND (tourneysPlayersId IS NULL)
- ORDER BY handStart"""
- elif(self.dbname == 'SQLite'):
- #Probably doesn't work.
- self.query['getRingWinningsAllGamesPlayerIdSite'] = """SELECT handId, winnings FROM HandsPlayers
- INNER JOIN Players ON HandsPlayers.playerId = Players.id
- INNER JOIN Hands ON Hands.id = HandsPlayers.handId
- WHERE Players.name = %s AND Players.siteId = %s AND (tourneysPlayersId IS NULL)
- ORDER BY handStart"""
+ # Returns all cash game handIds and the money won(winnings is the final pot)
+ # by the playerId for a single site.
+ if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
+ self.query['getRingWinningsAllGamesPlayerIdSite'] = """SELECT handId, winnings FROM HandsPlayers
+ INNER JOIN Players ON HandsPlayers.playerId = Players.id
+ INNER JOIN Hands ON Hands.id = HandsPlayers.handId
+ WHERE Players.name = %s AND Players.siteId = %s AND (tourneysPlayersId IS NULL)
+ ORDER BY handStart"""
+ elif(self.dbname == 'SQLite'):
+ #Probably doesn't work.
+ self.query['getRingWinningsAllGamesPlayerIdSite'] = """SELECT handId, winnings FROM HandsPlayers
+ INNER JOIN Players ON HandsPlayers.playerId = Players.id
+ INNER JOIN Hands ON Hands.id = HandsPlayers.handId
+ WHERE Players.name = %s AND Players.siteId = %s AND (tourneysPlayersId IS NULL)
+ ORDER BY handStart"""
- # Returns the profit for a given ring game handId, Total pot - money invested by playerId
- if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
- self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
- INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
- INNER JOIN Players ON HandsPlayers.playerId = Players.id
- WHERE Players.name = %s AND HandsPlayers.handId = %s
- AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
- elif(self.dbname == 'SQLite'):
- #Probably doesn't work.
- self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
- INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
- INNER JOIN Players ON HandsPlayers.playerId = Players.id
- WHERE Players.name = %s AND HandsPlayers.handId = %s
- AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
+ # Returns the profit for a given ring game handId, Total pot - money invested by playerId
+ if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
+ self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
+ INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
+ INNER JOIN Players ON HandsPlayers.playerId = Players.id
+ WHERE Players.name = %s AND HandsPlayers.handId = %s
+ AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
+ elif(self.dbname == 'SQLite'):
+ #Probably doesn't work.
+ self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
+ INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
+ INNER JOIN Players ON HandsPlayers.playerId = Players.id
+ WHERE Players.name = %s AND HandsPlayers.handId = %s
+ AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
- if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
- self.query['getRingProfitAllHandsPlayerIdSite'] = """
- SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount)
- FROM HandsPlayers hp
- INNER JOIN Players pl ON hp.playerId = pl.id
- INNER JOIN Hands h ON h.id = hp.handId
- INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id
- WHERE pl.name = %s
- AND pl.siteId = %s
- AND hp.tourneysPlayersId IS NULL
- GROUP BY hp.handId, hp.winnings, h.handStart
- ORDER BY h.handStart"""
- elif(self.dbname == 'SQLite'):
- #Probably doesn't work.
- self.query['getRingProfitAllHandsPlayerIdSite'] = """
- SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount)
- FROM HandsPlayers hp
- INNER JOIN Players pl ON hp.playerId = pl.id
- INNER JOIN Hands h ON h.id = hp.handId
- INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id
- WHERE pl.name = %s
- AND pl.siteId = %s
- AND hp.tourneysPlayersId IS NULL
- GROUP BY hp.handId, hp.winnings, h.handStart
- ORDER BY h.handStart"""
+ if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
+ self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
+ elif(self.dbname == 'SQLite'):
+ self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
- # Returns the profit for a given ring game handId, Total pot - money invested by playerId - WRONG, returns players costs
- if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
- self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
- INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
- INNER JOIN Players ON HandsPlayers.playerId = Players.id
- WHERE Players.name = %s AND HandsPlayers.handId = %s
- AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
- elif(self.dbname == 'SQLite'):
- #Probably doesn't work.
- self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
- INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
- INNER JOIN Players ON HandsPlayers.playerId = Players.id
- WHERE Players.name = %s AND HandsPlayers.handId = %s
- AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
-
- # Returns a list of the tables in the database
- if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
- self.query['getTableList'] = """
- SELECT table_name
- FROM information_schema.tables
- WHERE table_type = 'BASE TABLE'
- AND table_schema = %s
- """
- elif(self.dbname == 'getTableList'):
- #Probably doesn't work.
- self.query['getTableList'] = """
- SELECT table_name
- FROM information_schema.tables
- WHERE table_type = 'BASE TABLE'
- AND table_schema = %s
- """
+ if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
+ self.query['getRingProfitAllHandsPlayerIdSite'] = """
+ SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount)
+ FROM HandsPlayers hp
+ INNER JOIN Players pl ON hp.playerId = pl.id
+ INNER JOIN Hands h ON h.id = hp.handId
+ INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id
+ WHERE pl.name = %s
+ AND pl.siteId = %s
+ AND hp.tourneysPlayersId IS NULL
+ GROUP BY hp.handId, hp.winnings, h.handStart
+ ORDER BY h.handStart"""
+ elif(self.dbname == 'SQLite'):
+ #Probably doesn't work.
+ self.query['getRingProfitAllHandsPlayerIdSite'] = """
+ SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount)
+ FROM HandsPlayers hp
+ INNER JOIN Players pl ON hp.playerId = pl.id
+ INNER JOIN Hands h ON h.id = hp.handId
+ INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id
+ WHERE pl.name = %s
+ AND pl.siteId = %s
+ AND hp.tourneysPlayersId IS NULL
+ GROUP BY hp.handId, hp.winnings, h.handStart
+ ORDER BY h.handStart"""
if(self.dbname == 'MySQL InnoDB'):
self.query['playerStats'] = """
@@ -733,8 +703,7 @@ class FpdbSQLQueries:
from Gametypes gt
inner join Sites s on s.Id = gt.siteId
inner join HudCache hc on hc.gameTypeId = gt.Id
- where gt.limittype = 'nl'
- and hc.playerId in (3) # use here?
+ where hc.playerId in
# use here ?
group by hc.gametypeId
) stats
@@ -747,7 +716,7 @@ class FpdbSQLQueries:
from HandsPlayers hp
inner join Hands h ON h.id = hp.handId
inner join HandsActions ha ON ha.handPlayerId = hp.id
- where hp.playerId in (3) # use here?
+ where hp.playerId in
# use here ?
and hp.tourneysPlayersId IS NULL
group by hp.handId, h.gameTypeId, hp.position, hp.winnings
diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py
index 9f07910c..bb5e0386 100644
--- a/pyfpdb/GuiAutoImport.py
+++ b/pyfpdb/GuiAutoImport.py
@@ -1,211 +1,211 @@
-#!/usr/bin/python
-
-#Copyright 2008 Steffen Jobbagy-Felso
-#This program is free software: you can redistribute it and/or modify
-#it under the terms of the GNU Affero General Public License as published by
-#the Free Software Foundation, version 3 of the License.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU Affero General Public License
-#along with this program. If not, see .
-#In the "official" distribution you can find the license in
-#agpl-3.0.txt in the docs folder of the package.
-
-import threading
-import subprocess
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-import gobject
-import os
-import time
-import fpdb_import
-
-
-class GuiAutoImport (threading.Thread):
- def __init__(self, settings, config):
- """Constructor for GuiAutoImport"""
- self.settings=settings
- self.config=config
-
- imp = self.config.get_import_parameters()
-
- print "Import parameters"
- print imp
-
- self.input_settings = {}
-
- self.importer = fpdb_import.Importer(self, self.settings, self.config)
- self.importer.setCallHud(True)
- self.importer.setMinPrint(30)
- self.importer.setQuiet(False)
- self.importer.setFailOnError(False)
- self.importer.setHandCount(0)
-# self.importer.setWatchTime()
-
- self.server=settings['db-host']
- self.user=settings['db-user']
- self.password=settings['db-password']
- self.database=settings['db-databaseName']
-
- self.mainVBox=gtk.VBox(False,1)
- self.mainVBox.show()
-
- self.settingsHBox = gtk.HBox(False, 0)
- self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
- self.settingsHBox.show()
-
- self.intervalLabel = gtk.Label("Time between imports in seconds:")
- self.settingsHBox.pack_start(self.intervalLabel)
- self.intervalLabel.show()
-
- self.intervalEntry=gtk.Entry()
- self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval")))
- self.settingsHBox.pack_start(self.intervalEntry)
- self.intervalEntry.show()
-
- self.addSites(self.mainVBox)
-
- self.startButton=gtk.Button("Start Autoimport")
- self.startButton.connect("clicked", self.startClicked, "start clicked")
- self.mainVBox.add(self.startButton)
- self.startButton.show()
-
-
- #end of GuiAutoImport.__init__
- def browseClicked(self, widget, data):
- """runs when user clicks one of the browse buttons in the auto import tab"""
- current_path=data[1].get_text()
-
- dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import",
- action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
- buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
- #dia_chooser.set_current_folder(pathname)
- dia_chooser.set_filename(current_path)
- #dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import
-
- response = dia_chooser.run()
- if response == gtk.RESPONSE_OK:
- #print dia_chooser.get_filename(), 'selected'
- data[1].set_text(dia_chooser.get_filename())
- self.input_settings[data[0]][0] = dia_chooser.get_filename()
- elif response == gtk.RESPONSE_CANCEL:
- print 'Closed, no files selected'
- dia_chooser.destroy()
- #end def GuiAutoImport.browseClicked
-
- def do_import(self):
- """Callback for timer to do an import iteration."""
- self.importer.runUpdated()
- print "GuiAutoImport.import_dir done"
- return True
-
- def startClicked(self, widget, data):
- """runs when user clicks start on auto import tab"""
-
-# Check to see if we have an open file handle to the HUD and open one if we do not.
-# bufsize = 1 means unbuffered
-# We need to close this file handle sometime.
-
-# TODO: Allow for importing from multiple dirs - REB 29AUG2008
-# As presently written this function does nothing if there is already a pipe open.
-# That is not correct. It should open another dir for importing while piping the
-# results to the same pipe. This means that self.path should be a a list of dirs
-# to watch.
- try: #uhhh, I don't this this is the best way to check for the existence of an attr
- getattr(self, "pipe_to_hud")
- except AttributeError:
- if os.name == 'nt':
- command = "python HUD_main.py" + " %s" % (self.database)
- bs = 0 # windows is not happy with line buffing here
- self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
- universal_newlines=True)
- else:
- cwd = os.getcwd()
- command = os.path.join(cwd, 'HUD_main.py')
- bs = 1
- self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
- universal_newlines=True)
-# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
-# universal_newlines=True)
-# command = command + " %s" % (self.database)
-# print "command = ", command
-# self.pipe_to_hud = os.popen(command, 'w')
-
-# Add directories to importer object.
- for site in self.input_settings:
- self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
- print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0])
- self.do_import()
-
- interval=int(self.intervalEntry.get_text())
- gobject.timeout_add(interval*1000, self.do_import)
- #end def GuiAutoImport.startClicked
-
- def get_vbox(self):
- """returns the vbox of this thread"""
- return self.mainVBox
- #end def get_vbox
-
- #Create the site line given required info and setup callbacks
- #enabling and disabling sites from this interface not possible
- #expects a box to layout the line horizontally
- def createSiteLine(self, hbox, site, iconpath, hhpath, filter_name, active = True):
- label = gtk.Label(site + " auto-import:")
- hbox.pack_start(label, False, False, 0)
- label.show()
-
- dirPath=gtk.Entry()
- dirPath.set_text(hhpath)
- hbox.pack_start(dirPath, False, True, 0)
- dirPath.show()
-
- browseButton=gtk.Button("Browse...")
- browseButton.connect("clicked", self.browseClicked, [site] + [dirPath])
- hbox.pack_start(browseButton, False, False, 0)
- browseButton.show()
-
- label = gtk.Label(site + " filter:")
- hbox.pack_start(label, False, False, 0)
- label.show()
-
- filter=gtk.Entry()
- filter.set_text(filter_name)
- hbox.pack_start(filter, False, True, 0)
- filter.show()
-
- def addSites(self, vbox):
- for site in self.config.supported_sites.keys():
- pathHBox = gtk.HBox(False, 0)
- vbox.pack_start(pathHBox, False, True, 0)
- pathHBox.show()
-
- paths = self.config.get_default_paths(site)
- params = self.config.get_site_parameters(site)
- self.createSiteLine(pathHBox, site, False, paths['hud-defaultPath'], params['converter'], params['enabled'])
- self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']]
-
-if __name__== "__main__":
- def destroy(*args): # call back for terminating the main eventloop
- gtk.main_quit()
-
- settings = {}
- settings['db-host'] = "192.168.1.100"
- settings['db-user'] = "mythtv"
- settings['db-password'] = "mythtv"
- settings['db-databaseName'] = "fpdb"
- settings['hud-defaultInterval'] = 10
- settings['hud-defaultPath'] = 'C:/Program Files/PokerStars/HandHistory/nutOmatic'
- settings['callFpdbHud'] = True
-
- i = GuiAutoImport(settings)
- main_window = gtk.Window()
- main_window.connect("destroy", destroy)
- main_window.add(i.mainVBox)
- main_window.show()
- gtk.main()
+#!/usr/bin/python
+
+#Copyright 2008 Steffen Jobbagy-Felso
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU Affero General Public License as published by
+#the Free Software Foundation, version 3 of the License.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU Affero General Public License
+#along with this program. If not, see .
+#In the "official" distribution you can find the license in
+#agpl-3.0.txt in the docs folder of the package.
+
+import threading
+import subprocess
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+import gobject
+import os
+import time
+import fpdb_import
+
+
+class GuiAutoImport (threading.Thread):
+ def __init__(self, settings, config):
+ """Constructor for GuiAutoImport"""
+ self.settings=settings
+ self.config=config
+
+ imp = self.config.get_import_parameters()
+
+ print "Import parameters"
+ print imp
+
+ self.input_settings = {}
+
+ self.importer = fpdb_import.Importer(self,self.settings, self.config)
+ self.importer.setCallHud(True)
+ self.importer.setMinPrint(30)
+ self.importer.setQuiet(False)
+ self.importer.setFailOnError(False)
+ self.importer.setHandCount(0)
+# self.importer.setWatchTime()
+
+ self.server=settings['db-host']
+ self.user=settings['db-user']
+ self.password=settings['db-password']
+ self.database=settings['db-databaseName']
+
+ self.mainVBox=gtk.VBox(False,1)
+ self.mainVBox.show()
+
+ self.settingsHBox = gtk.HBox(False, 0)
+ self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
+ self.settingsHBox.show()
+
+ self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:")
+ self.settingsHBox.pack_start(self.intervalLabel)
+ self.intervalLabel.show()
+
+ self.intervalEntry=gtk.Entry()
+ self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval")))
+ self.settingsHBox.pack_start(self.intervalEntry)
+ self.intervalEntry.show()
+
+ self.addSites(self.mainVBox)
+
+ self.startButton=gtk.Button("Start Autoimport")
+ self.startButton.connect("clicked", self.startClicked, "start clicked")
+ self.mainVBox.add(self.startButton)
+ self.startButton.show()
+
+
+ #end of GuiAutoImport.__init__
+ def browseClicked(self, widget, data):
+ """runs when user clicks one of the browse buttons in the auto import tab"""
+ current_path=data[1].get_text()
+
+ dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import",
+ action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
+ #dia_chooser.set_current_folder(pathname)
+ dia_chooser.set_filename(current_path)
+ #dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import
+
+ response = dia_chooser.run()
+ if response == gtk.RESPONSE_OK:
+ #print dia_chooser.get_filename(), 'selected'
+ data[1].set_text(dia_chooser.get_filename())
+ self.input_settings[data[0]][0] = dia_chooser.get_filename()
+ elif response == gtk.RESPONSE_CANCEL:
+ print 'Closed, no files selected'
+ dia_chooser.destroy()
+ #end def GuiAutoImport.browseClicked
+
+ def do_import(self):
+ """Callback for timer to do an import iteration."""
+ self.importer.runUpdated()
+ print "GuiAutoImport.import_dir done"
+ return True
+
+ def startClicked(self, widget, data):
+ """runs when user clicks start on auto import tab"""
+
+# Check to see if we have an open file handle to the HUD and open one if we do not.
+# bufsize = 1 means unbuffered
+# We need to close this file handle sometime.
+
+# TODO: Allow for importing from multiple dirs - REB 29AUG2008
+# As presently written this function does nothing if there is already a pipe open.
+# That is not correct. It should open another dir for importing while piping the
+# results to the same pipe. This means that self.path should be a a list of dirs
+# to watch.
+ try: #uhhh, I don't this this is the best way to check for the existence of an attr
+ getattr(self, "pipe_to_hud")
+ except AttributeError:
+ if os.name == 'nt':
+ command = "python HUD_main.py" + " %s" % (self.database)
+ bs = 0 # windows is not happy with line buffing here
+ self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
+ universal_newlines=True)
+ else:
+ cwd = os.getcwd()
+ command = os.path.join(cwd, 'HUD_main.py')
+ bs = 1
+ self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
+ universal_newlines=True)
+# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
+# universal_newlines=True)
+# command = command + " %s" % (self.database)
+# print "command = ", command
+# self.pipe_to_hud = os.popen(command, 'w')
+
+# Add directories to importer object.
+ for site in self.input_settings:
+ self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
+ print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0])
+ self.do_import()
+
+ interval=int(self.intervalEntry.get_text())
+ gobject.timeout_add(interval*1000, self.do_import)
+ #end def GuiAutoImport.startClicked
+
+ def get_vbox(self):
+ """returns the vbox of this thread"""
+ return self.mainVBox
+ #end def get_vbox
+
+ #Create the site line given required info and setup callbacks
+ #enabling and disabling sites from this interface not possible
+ #expects a box to layout the line horizontally
+ def createSiteLine(self, hbox, site, iconpath, hhpath, filter_name, active = True):
+ label = gtk.Label(site + " auto-import:")
+ hbox.pack_start(label, False, False, 0)
+ label.show()
+
+ dirPath=gtk.Entry()
+ dirPath.set_text(hhpath)
+ hbox.pack_start(dirPath, False, True, 0)
+ dirPath.show()
+
+ browseButton=gtk.Button("Browse...")
+ browseButton.connect("clicked", self.browseClicked, [site] + [dirPath])
+ hbox.pack_start(browseButton, False, False, 0)
+ browseButton.show()
+
+ label = gtk.Label(site + " filter:")
+ hbox.pack_start(label, False, False, 0)
+ label.show()
+
+ filter=gtk.Entry()
+ filter.set_text(filter_name)
+ hbox.pack_start(filter, False, True, 0)
+ filter.show()
+
+ def addSites(self, vbox):
+ for site in self.config.supported_sites.keys():
+ pathHBox = gtk.HBox(False, 0)
+ vbox.pack_start(pathHBox, False, True, 0)
+ pathHBox.show()
+
+ paths = self.config.get_default_paths(site)
+ params = self.config.get_site_parameters(site)
+ self.createSiteLine(pathHBox, site, False, paths['hud-defaultPath'], params['converter'], params['enabled'])
+ self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']]
+
+if __name__== "__main__":
+ def destroy(*args): # call back for terminating the main eventloop
+ gtk.main_quit()
+
+ settings = {}
+ settings['db-host'] = "192.168.1.100"
+ settings['db-user'] = "mythtv"
+ settings['db-password'] = "mythtv"
+ settings['db-databaseName'] = "fpdb"
+ settings['hud-defaultInterval'] = 10
+ settings['hud-defaultPath'] = 'C:/Program Files/PokerStars/HandHistory/nutOmatic'
+ settings['callFpdbHud'] = True
+
+ i = GuiAutoImport(settings)
+ main_window = gtk.Window()
+ main_window.connect("destroy", destroy)
+ main_window.add(i.mainVBox)
+ main_window.show()
+ gtk.main()
diff --git a/pyfpdb/GuiBulkImport.py b/pyfpdb/GuiBulkImport.py
index ee5af2c6..e3bee81c 100644
--- a/pyfpdb/GuiBulkImport.py
+++ b/pyfpdb/GuiBulkImport.py
@@ -22,14 +22,17 @@ import pygtk
pygtk.require('2.0')
import gtk
import os #todo: remove this once import_dir is in fpdb_import
+from time import time
class GuiBulkImport (threading.Thread):
def import_dir(self):
"""imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it"""
self.path=self.inputFile
self.importer.addImportDirectory(self.path)
+ self.importer.setCallHud(False)
+ starttime = time()
self.importer.runImport()
- print "GuiBulkImport.import_dir done"
+ print "GuiBulkImport.import_dir done in %s" %(time() - starttime)
def load_clicked(self, widget, data=None):
self.inputFile=self.chooser.get_filename()
@@ -64,6 +67,7 @@ class GuiBulkImport (threading.Thread):
self.import_dir()
else:
self.importer.addImportFile(self.inputFile)
+ self.importer.setCallHud(False)
self.importer.runImport()
self.importer.clearFileList()
@@ -80,7 +84,7 @@ class GuiBulkImport (threading.Thread):
self.db=db
self.settings=settings
self.config=config
- self.importer = fpdb_import.Importer(self,self.settings)
+ self.importer = fpdb_import.Importer(self,self.settings, config)
self.vbox=gtk.VBox(False,1)
self.vbox.show()
diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py
index e4f9270e..d50f6b6a 100644
--- a/pyfpdb/GuiGraphViewer.py
+++ b/pyfpdb/GuiGraphViewer.py
@@ -20,130 +20,304 @@ import pygtk
pygtk.require('2.0')
import gtk
import os
+from time import time
#import pokereval
try:
- from matplotlib.figure import Figure
- from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
- from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
- from numpy import arange, cumsum
- from pylab import *
+ import matplotlib
+ matplotlib.use('GTK')
+ from matplotlib.figure import Figure
+ from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
+ from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
+ from numpy import arange, cumsum
+ from pylab import *
except:
- print "Failed to load libs for graphing, graphing will not function. Please install numpy and matplotlib if you want to use graphs."
- print "This is of no consequence for other parts of the program, e.g. import and HUD are NOT affected by this problem."
+ print """Failed to load libs for graphing, graphing will not function. Please in
+ stall numpy and matplotlib if you want to use graphs."""
+ print """This is of no consequence for other parts of the program, e.g. import
+ and HUD are NOT affected by this problem."""
import fpdb_import
import fpdb_db
class GuiGraphViewer (threading.Thread):
- def get_vbox(self):
- """returns the vbox of this thread"""
- return self.mainVBox
- #end def get_vbox
-
- def showClicked(self, widget, data):
- try: self.canvas.destroy()
- except AttributeError: pass
+ def get_vbox(self):
+ """returns the vbox of this thread"""
+ return self.mainHBox
+ #end def get_vbox
- name=self.nameEntry.get_text()
-
- site=self.siteEntry.get_text()
-
- if site=="PS":
- site=2
- sitename="PokerStars: "
- elif site=="FTP":
- site=1
- sitename="Full Tilt: "
- else:
- print "invalid text in site selection in graph, defaulting to PS"
- site=2
-
- self.fig = Figure(figsize=(5,4), dpi=100)
+ def generateGraph(self, widget, data):
+ try: self.canvas.destroy()
+ except AttributeError: pass
- #Set graph properties
- self.ax = self.fig.add_subplot(111)
+ # Whaich sites are selected?
+ # TODO:
+ # What hero names for the selected site?
+ # TODO:
- #
- self.ax.set_title("Profit graph for ring games")
+ name = self.heroes[self.sites]
- #Set axis labels and grid overlay properites
- self.ax.set_xlabel("Hands", fontsize = 12)
- self.ax.set_ylabel("$", fontsize = 12)
- self.ax.grid(color='g', linestyle=':', linewidth=0.2)
- text = "All Hands, " + sitename + str(name)
+ if self.sites == "PokerStars":
+ site=2
+ sitename="PokerStars: "
+ elif self.sites=="Full Tilt":
+ site=1
+ sitename="Full Tilt: "
+ else:
+ print "invalid text in site selection in graph, defaulting to PS"
+ site=2
- self.ax.annotate (text, (61,25), xytext =(0.1, 0.9) , textcoords ="axes fraction" ,)
+ self.fig = Figure(figsize=(5,4), dpi=100)
- #Get graph data from DB
- line = self.getRingProfitGraph(name, site)
+ #Set graph properties
+ self.ax = self.fig.add_subplot(111)
- #Draw plot
- self.ax.plot(line,)
+ #Get graph data from DB
+ starttime = time()
+ line = self.getRingProfitGraph(name, site)
+ print "Graph generated in: %s" %(time() - starttime)
- self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
- self.mainVBox.pack_start(self.canvas)
- self.canvas.show()
- #end of def showClicked
+ self.ax.set_title("Profit graph for ring games")
- def getRingProfitGraph(self, name, site):
- #self.cursor.execute(self.sql.query['getRingWinningsAllGamesPlayerIdSite'], (name, site))
- self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site))
- # returns (HandId,Winnings,Costs,Profit)
- winnings = self.db.cursor.fetchall()
+ #Set axis labels and grid overlay properites
+ self.ax.set_xlabel("Hands", fontsize = 12)
+ self.ax.set_ylabel("$", fontsize = 12)
+ self.ax.grid(color='g', linestyle=':', linewidth=0.2)
+ #This line will crash if no hands exist in the query.
+ text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
- #profit=range(len(winnings))
- #for i in profit:
- # self.cursor.execute(self.sql.query['getRingProfitFromHandId'], (name, winnings[i][0], site))
- # spent = self.db.cursor.fetchone()
- # profit[i]=(i, winnings[i][1]-spent[0])
-
- #y=map(lambda x:float(x[1]), profit)
- y=map(lambda x:float(x[3]), winnings)
+ self.ax.annotate(text,
+ xy=(10, -10),
+ xycoords='axes points',
+ horizontalalignment='left', verticalalignment='top',
+ fontsize=10)
- line = cumsum(y)
- return line/100
+
+ #Draw plot
+ self.ax.plot(line,)
+
+ self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
+ self.graphBox.add(self.canvas)
+ self.canvas.show()
+ #end of def showClicked
+
+ def getRingProfitGraph(self, name, site):
+ self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site))
+ #returns (HandId,Winnings,Costs,Profit)
+ winnings = self.db.cursor.fetchall()
+
+ y=map(lambda x:float(x[3]), winnings)
+ line = cumsum(y)
+ return line/100
#end of def getRingProfitGraph
- def __init__(self, db, settings, querylist, config, debug=True):
- """Constructor for GraphViewer"""
- self.debug=debug
- #print "start of GraphViewer constructor"
- self.db=db
- self.cursor=db.cursor
- self.settings=settings
- self.sql=querylist
-
- self.mainVBox = gtk.VBox(False, 0)
- self.mainVBox.show()
-
- self.settingsHBox = gtk.HBox(False, 0)
- self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
- self.settingsHBox.show()
-
- self.nameLabel = gtk.Label("Name of the player to be graphed:")
- self.settingsHBox.pack_start(self.nameLabel)
- self.nameLabel.show()
-
- self.nameEntry=gtk.Entry()
- self.nameEntry.set_text("name")
- self.settingsHBox.pack_start(self.nameEntry)
- self.nameEntry.show()
-
- self.siteLabel = gtk.Label("Site (PS or FTP):")
- self.settingsHBox.pack_start(self.siteLabel)
- self.siteLabel.show()
-
- self.siteEntry=gtk.Entry()
- self.siteEntry.set_text("PS")
- self.settingsHBox.pack_start(self.siteEntry)
- self.siteEntry.show()
+ def createPlayerLine(self, hbox, site, player):
+ label = gtk.Label(site +" id:")
+ hbox.pack_start(label, False, False, 0)
+ label.show()
- #Note: Assumes PokerStars is in the config
- self.nameEntry.set_text(config.supported_sites["PokerStars"].screen_name)
-
- self.showButton=gtk.Button("Show/Refresh")
- self.showButton.connect("clicked", self.showClicked, "show clicked")
- self.settingsHBox.pack_start(self.showButton)
- self.showButton.show()
- #end of GuiGraphViewer.__init__
+ pname = gtk.Entry()
+ pname.set_text(player)
+ pname.set_width_chars(20)
+ hbox.pack_start(pname, False, True, 0)
+ #TODO: Need to connect a callback here
+ pname.connect("changed", self.__set_hero_name, site)
+ #TODO: Look at GtkCompletion - to fill out usernames
+ pname.show()
+
+ self.__set_hero_name(pname, site)
+
+ def __set_hero_name(self, w, site):
+ self.heroes[site] = w.get_text()
+ print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
+
+ def createSiteLine(self, hbox, site):
+ cb = gtk.CheckButton(site)
+ cb.connect('clicked', self.__set_site_select, site)
+ hbox.pack_start(cb, False, False, 0)
+ cb.show()
+
+ def __set_site_select(self, w, site):
+ # This doesn't behave as intended - self.site only allows 1 site for the moment.
+ self.sites = site
+ print "self.sites set to %s" %(self.sites)
+
+ def fillPlayerFrame(self, vbox):
+ for site in self.conf.supported_sites.keys():
+ pathHBox = gtk.HBox(False, 0)
+ vbox.pack_start(pathHBox, False, True, 0)
+ pathHBox.show()
+
+ player = self.conf.supported_sites[site].screen_name
+ self.createPlayerLine(pathHBox, site, player)
+
+ def fillSitesFrame(self, vbox):
+ for site in self.conf.supported_sites.keys():
+ hbox = gtk.HBox(False, 0)
+ vbox.pack_start(hbox, False, True, 0)
+ hbox.show()
+ self.createSiteLine(hbox, site)
+
+ def fillDateFrame(self, vbox):
+ # Hat tip to Mika Bostrom - calendar code comes from PokerStats
+ hbox = gtk.HBox()
+ vbox.pack_start(hbox, False, True, 0)
+ hbox.show()
+
+ lbl_start = gtk.Label('From:')
+ lbl_start.show()
+
+ btn_start = gtk.Button()
+ btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
+ btn_start.connect('clicked', self.__calendar_dialog, self.start_date)
+ btn_start.show()
+
+ hbox.pack_start(lbl_start, expand=False, padding=3)
+ hbox.pack_start(btn_start, expand=False, padding=3)
+ hbox.pack_start(self.start_date, expand=False, padding=2)
+ self.start_date.show()
+
+ #New row for end date
+ hbox = gtk.HBox()
+ vbox.pack_start(hbox, False, True, 0)
+ hbox.show()
+
+ lbl_end = gtk.Label(' To:')
+ lbl_end.show()
+ btn_end = gtk.Button()
+ btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
+ btn_end.connect('clicked', self.__calendar_dialog, self.end_date)
+ btn_end.show()
+
+ btn_clear = gtk.Button(label=' Clear Dates ')
+ btn_clear.connect('clicked', self.__clear_dates)
+ btn_clear.show()
+
+ hbox.pack_start(lbl_end, expand=False, padding=3)
+ hbox.pack_start(btn_end, expand=False, padding=3)
+ hbox.pack_start(self.end_date, expand=False, padding=2)
+ self.end_date.show()
+
+ hbox.pack_start(btn_clear, expand=False, padding=15)
+
+ def __calendar_dialog(self, widget, entry):
+ d = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ d.set_title('Pick a date')
+
+ vb = gtk.VBox()
+ cal = gtk.Calendar()
+ vb.pack_start(cal, expand=False, padding=0)
+
+ btn = gtk.Button('Done')
+ btn.connect('clicked', self.__get_date, cal, entry, d)
+
+ vb.pack_start(btn, expand=False, padding=4)
+
+ d.add(vb)
+ d.set_position(gtk.WIN_POS_MOUSE)
+ d.show_all()
+
+ def __clear_dates(self, w):
+ self.start_date.set_text('')
+ self.end_date.set_text('')
+
+ def __get_dates(self):
+ t1 = self.start_date.get_text()
+ t2 = self.end_date.get_text()
+ return (t1, t2)
+
+ def __get_date(self, widget, calendar, entry, win):
+# year and day are correct, month is 0..11
+ (year, month, day) = calendar.get_date()
+ month += 1
+ ds = '%04d-%02d-%02d' % (year, month, day)
+ entry.set_text(ds)
+ win.destroy()
+
+ def exportGraph (self, widget, data):
+ dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:",
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
+
+ response = dia_chooser.run()
+ if response == gtk.RESPONSE_OK:
+ self.exportDir = dia_chooser.get_filename()
+ elif response == gtk.RESPONSE_CANCEL:
+ print 'Closed, no graph exported'
+ dia_chooser.destroy()
+
+ def __init__(self, db, settings, querylist, config, debug=True):
+ """Constructor for GraphViewer"""
+ self.debug=debug
+ #print "start of GraphViewer constructor"
+ self.db=db
+ self.cursor=db.cursor
+ self.settings=settings
+ self.sql=querylist
+ self.conf = config
+
+ self.sites = "PokerStars"
+ self.heroes = {}
+
+ # For use in date ranges.
+ self.start_date = gtk.Entry(max=12)
+ self.end_date = gtk.Entry(max=12)
+ self.start_date.set_property('editable', False)
+ self.end_date.set_property('editable', False)
+
+ self.mainHBox = gtk.HBox(False, 0)
+ self.mainHBox.show()
+
+ self.leftPanelBox = gtk.VBox(False, 0)
+ self.graphBox = gtk.VBox(False, 0)
+
+ self.hpane = gtk.HPaned()
+ self.hpane.pack1(self.leftPanelBox)
+ self.hpane.pack2(self.graphBox)
+ self.hpane.show()
+
+ self.mainHBox.add(self.hpane)
+
+ playerFrame = gtk.Frame("Hero:")
+ playerFrame.set_label_align(0.0, 0.0)
+ playerFrame.show()
+ vbox = gtk.VBox(False, 0)
+ vbox.show()
+
+ self.fillPlayerFrame(vbox)
+ playerFrame.add(vbox)
+
+ sitesFrame = gtk.Frame("Sites:")
+ sitesFrame.set_label_align(0.0, 0.0)
+ sitesFrame.show()
+ vbox = gtk.VBox(False, 0)
+ vbox.show()
+
+ self.fillSitesFrame(vbox)
+ sitesFrame.add(vbox)
+
+ dateFrame = gtk.Frame("Date:")
+ dateFrame.set_label_align(0.0, 0.0)
+ dateFrame.show()
+ vbox = gtk.VBox(False, 0)
+ vbox.show()
+
+ self.fillDateFrame(vbox)
+ dateFrame.add(vbox)
+
+ graphButton=gtk.Button("Generate Graph")
+ graphButton.connect("clicked", self.generateGraph, "cliced data")
+ graphButton.show()
+
+ self.exportButton=gtk.Button("Export to File")
+ self.exportButton.connect("clicked", self.exportGraph, "show clicked")
+ self.exportButton.show()
+
+ self.leftPanelBox.add(playerFrame)
+ self.leftPanelBox.add(sitesFrame)
+ self.leftPanelBox.add(dateFrame)
+ self.leftPanelBox.add(graphButton)
+ self.leftPanelBox.add(self.exportButton)
+
+ self.leftPanelBox.show()
+ self.graphBox.show()
diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py
new file mode 100644
index 00000000..7d689228
--- /dev/null
+++ b/pyfpdb/GuiPlayerStats.py
@@ -0,0 +1,165 @@
+#!/usr/bin/python
+
+#Copyright 2008 Steffen Jobbagy-Felso
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU Affero General Public License as published by
+#the Free Software Foundation, version 3 of the License.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU Affero General Public License
+#along with this program. If not, see .
+#In the "official" distribution you can find the license in
+#agpl-3.0.txt in the docs folder of the package.
+
+import threading
+import pygtk
+pygtk.require('2.0')
+import gtk
+import os
+
+import fpdb_import
+import fpdb_db
+import FpdbSQLQueries
+
+class GuiPlayerStats (threading.Thread):
+ def get_vbox(self):
+ """returns the vbox of this thread"""
+ return self.main_hbox
+
+ def toggleCallback(self, widget, data=None):
+# print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
+ self.activesite = data
+ print "DEBUG: activesite set to %s" %(self.activesite)
+
+ def refreshStats(self, widget, data):
+ try: self.stats_table.destroy()
+ except AttributeError: pass
+ self.fillStatsFrame(self.stats_frame)
+
+ def fillStatsFrame(self, vbox):
+ # Get currently active site and grab playerid
+ tmp = self.sql.query['playerStats']
+
+ result = self.cursor.execute(self.sql.query['getPlayerId'], self.heroes[self.activesite])
+ result = self.db.cursor.fetchall()
+ pid = result[0][0]
+ tmp = tmp.replace("", "(" + str(pid) + ")")
+ self.cursor.execute(tmp)
+ result = self.db.cursor.fetchall()
+ cols = 18
+ rows = len(result)+1 # +1 for title row
+ self.stats_table = gtk.Table(rows, cols, False)
+ self.stats_table.set_col_spacings(4)
+ self.stats_table.show()
+ vbox.add(self.stats_table)
+
+ # Create header row
+ titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100")
+
+ col = 0
+ row = 0
+ for t in titles:
+ l = gtk.Label(titles[col])
+ l.show()
+ self.stats_table.attach(l, col, col+1, row, row+1)
+ col +=1
+
+ for row in range(rows-1):
+ for col in range(cols):
+ if(row%2 == 0):
+ bgcolor = "white"
+ else:
+ bgcolor = "lightgrey"
+ eb = gtk.EventBox()
+ eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
+ l = gtk.Label(result[row-1][col])
+ eb.add(l)
+ self.stats_table.attach(eb, col, col+1, row+1, row+2)
+ l.show()
+ eb.show()
+
+
+ def fillPlayerFrame(self, vbox):
+ for site in self.conf.supported_sites.keys():
+ hbox = gtk.HBox(False, 0)
+ vbox.pack_start(hbox, False, True, 0)
+ hbox.show()
+
+ player = self.conf.supported_sites[site].screen_name
+ self.createPlayerLine(hbox, site, player)
+ hbox = gtk.HBox(False, 0)
+ button = gtk.Button("Refresh")
+ button.connect("clicked", self.refreshStats, False)
+ button.show()
+ hbox.add(button)
+ vbox.pack_start(hbox, False, True, 0)
+ hbox.show()
+
+ def createPlayerLine(self, hbox, site, player):
+ if(self.buttongroup == None):
+ button = gtk.RadioButton(None, site + " id:")
+ button.set_active(True)
+ self.buttongroup = button
+ self.activesite = site
+ else:
+ button = gtk.RadioButton(self.buttongroup, site + " id:")
+ hbox.pack_start(button, True, True, 0)
+ button.connect("toggled", self.toggleCallback, site)
+ button.show()
+
+ pname = gtk.Entry()
+ pname.set_text(player)
+ pname.set_width_chars(20)
+ hbox.pack_start(pname, False, True, 0)
+ pname.connect("changed", self.__set_hero_name, site)
+ #TODO: Look at GtkCompletion - to fill out usernames
+ pname.show()
+ self.__set_hero_name(pname, site)
+
+ def __set_hero_name(self, w, site):
+ self.heroes[site] = w.get_text()
+ print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
+
+ def __init__(self, db, config, querylist, debug=True):
+ self.debug=debug
+ self.db=db
+ self.cursor=db.cursor
+ self.conf=config
+
+ self.sql = querylist
+
+ self.activesite = None
+ self.buttongroup = None
+
+ self.heroes = {}
+ self.stat_table = None
+ self.stats_frame = None
+
+ self.main_hbox = gtk.HBox(False, 0)
+ self.main_hbox.show()
+
+ playerFrame = gtk.Frame("Hero:")
+ playerFrame.set_label_align(0.0, 0.0)
+ playerFrame.show()
+ vbox = gtk.VBox(False, 0)
+ vbox.show()
+
+ self.fillPlayerFrame(vbox)
+ playerFrame.add(vbox)
+
+ statsFrame = gtk.Frame("Stats:")
+ statsFrame.set_label_align(0.0, 0.0)
+ statsFrame.show()
+ self.stats_frame = gtk.VBox(False, 0)
+ self.stats_frame.show()
+
+ self.fillStatsFrame(self.stats_frame)
+ statsFrame.add(self.stats_frame)
+
+ self.main_hbox.pack_start(playerFrame)
+ self.main_hbox.pack_start(statsFrame)
+
diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example
index 3a595fe2..355c9d2f 100644
--- a/pyfpdb/HUD_config.xml.example
+++ b/pyfpdb/HUD_config.xml.example
@@ -2,7 +2,7 @@
-
+
@@ -49,7 +49,7 @@
-
+
@@ -84,7 +84,7 @@
-
+
@@ -187,7 +187,7 @@
-
+
diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py
index c0244e8d..0251c7cf 100644
--- a/pyfpdb/HUD_main.py
+++ b/pyfpdb/HUD_main.py
@@ -1,171 +1,171 @@
-#!/usr/bin/env python
-
-"""Hud_main.py
-
-Main for FreePokerTools HUD.
-"""
-# Copyright 2008, 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
-
-########################################################################
-
-# to do kill window on my seat
-# to do adjust for preferred seat
-# to do allow window resizing
-# to do hud to echo, but ignore non numbers
-# to do no hud window for hero
-# to do things to add to config.xml
-# to do font and size
-# to do opacity
-
-# Standard Library modules
-import sys
-import os
-import thread
-import time
-import string
-import re
-
-errorfile = open('HUD-error.txt', 'w', 0)
-sys.stderr = errorfile
-
-# pyGTK modules
-import pygtk
-import gtk
-import gobject
-
-# FreePokerTools modules
-import Configuration
-import Database
-import Tables
-import Hud
-
-# global dict for keeping the huds
-hud_dict = {}
-
-db_connection = 0;
-config = 0;
-
-def destroy(*args): # call back for terminating the main eventloop
- gtk.main_quit()
-
-def create_HUD(new_hand_id, table, db_name, table_name, max, poker_game, db_connection, config, stat_dict):
- global hud_dict
- def idle_func():
- global hud_dict
- gtk.gdk.threads_enter()
- try:
- hud_dict[table_name] = Hud.Hud(table, max, poker_game, config, db_name)
- hud_dict[table_name].create(new_hand_id, config)
- hud_dict[table_name].update(new_hand_id, config, stat_dict)
- hud_dict[table_name].reposition_windows()
- return False
- finally:
- gtk.gdk.threads_leave()
- gobject.idle_add(idle_func)
-
-def update_HUD(new_hand_id, table_name, config, stat_dict):
- global hud_dict
- def idle_func():
- gtk.gdk.threads_enter()
- try:
- hud_dict[table_name].update(new_hand_id, config, stat_dict)
- for m in hud_dict[table_name].aux_windows:
- m.update_gui(new_hand_id)
- return False
- finally:
- gtk.gdk.threads_leave()
- gobject.idle_add(idle_func)
-
-def read_stdin(): # This is the thread function
- global hud_dict
-
- db_connection = Database.Database(config, db_name, 'temp')
- tourny_finder = re.compile('(\d+) (\d+)')
-
- while True: # wait for a new hand number on stdin
- new_hand_id = sys.stdin.readline()
- new_hand_id = string.rstrip(new_hand_id)
- if new_hand_id == "": # blank line means quit
- destroy()
-
-# delete hud_dict entries for any HUD destroyed since last iteration
- for h in hud_dict.keys():
- if hud_dict[h].deleted:
- del(hud_dict[h])
-
-# get basic info about the new hand from the db
- (table_name, max, poker_game) = db_connection.get_table_name(new_hand_id)
-
-# find out if this hand is from a tournament
- is_tournament = False
- (tour_number, tab_number) = (0, 0)
- mat_obj = tourny_finder.search(table_name)
-# if len(mat_obj.groups) == 2:
- if mat_obj:
- is_tournament = True
- (tour_number, tab_number) = mat_obj.group(1, 2)
-
- stat_dict = db_connection.get_stats_from_hand(new_hand_id)
-
-# if a hud for this CASH table exists, just update it
- if hud_dict.has_key(table_name):
-# update the data for the aux_windows
- for aw in hud_dict[table_name].aux_windows:
- aw.update_data(new_hand_id)
- update_HUD(new_hand_id, table_name, config, stat_dict)
-# if a hud for this TOURNAMENT table exists, just update it
- elif hud_dict.has_key(tour_number):
- update_HUD(new_hand_id, tour_number, config, stat_dict)
-# otherwise create a new hud
- else:
- if is_tournament:
- tablewindow = Tables.discover_tournament_table(config, tour_number, tab_number)
- if tablewindow == None:
- sys.stderr.write("tournament %s, table %s not found\n" % (tour_number, tab_number))
- else:
- create_HUD(new_hand_id, tablewindow, db_name, tour_number, max, poker_game, db_connection, config, stat_dict)
- else:
- tablewindow = Tables.discover_table_by_name(config, table_name)
- if tablewindow == None:
- sys.stderr.write("table name "+table_name+" not found\n")
- else:
- create_HUD(new_hand_id, tablewindow, db_name, table_name, max, poker_game, db_connection, config, stat_dict)
-
-if __name__== "__main__":
- sys.stderr.write("HUD_main starting\n")
-
- try:
- db_name = sys.argv[1]
- except:
- db_name = 'fpdb'
- sys.stderr.write("Using db name = %s\n" % (db_name))
-
- config = Configuration.Config()
-
- gobject.threads_init() # this is required
- thread.start_new_thread(read_stdin, ()) # starts the thread
-
- main_window = gtk.Window()
- main_window.connect("destroy", destroy)
- eb = gtk.EventBox()
- label = gtk.Label('Closing this window will exit from the HUD.')
- eb.add(label)
- main_window.add(eb)
- main_window.set_title("HUD Main Window")
- main_window.show_all()
-
- gtk.main()
+#!/usr/bin/env python
+
+"""Hud_main.py
+
+Main for FreePokerTools HUD.
+"""
+# Copyright 2008, 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
+
+########################################################################
+
+# to do kill window on my seat
+# to do adjust for preferred seat
+# to do allow window resizing
+# to do hud to echo, but ignore non numbers
+# to do no hud window for hero
+# to do things to add to config.xml
+# to do font and size
+# to do opacity
+
+# Standard Library modules
+import sys
+import os
+import thread
+import time
+import string
+import re
+
+errorfile = open('HUD-error.txt', 'w', 0)
+sys.stderr = errorfile
+
+# pyGTK modules
+import pygtk
+import gtk
+import gobject
+
+# FreePokerTools modules
+import Configuration
+import Database
+import Tables
+import Hud
+
+# global dict for keeping the huds
+hud_dict = {}
+
+db_connection = 0;
+config = 0;
+
+def destroy(*args): # call back for terminating the main eventloop
+ gtk.main_quit()
+
+def create_HUD(new_hand_id, table, db_name, table_name, max, poker_game, db_connection, config, stat_dict):
+ global hud_dict
+ def idle_func():
+ global hud_dict
+ gtk.gdk.threads_enter()
+ try:
+ hud_dict[table_name] = Hud.Hud(table, max, poker_game, config, db_name)
+ hud_dict[table_name].create(new_hand_id, config)
+ hud_dict[table_name].update(new_hand_id, config, stat_dict)
+ hud_dict[table_name].reposition_windows()
+ return False
+ finally:
+ gtk.gdk.threads_leave()
+ gobject.idle_add(idle_func)
+
+def update_HUD(new_hand_id, table_name, config, stat_dict):
+ global hud_dict
+ def idle_func():
+ gtk.gdk.threads_enter()
+ try:
+ hud_dict[table_name].update(new_hand_id, config, stat_dict)
+ for m in hud_dict[table_name].aux_windows:
+ m.update_gui(new_hand_id)
+ return False
+ finally:
+ gtk.gdk.threads_leave()
+ gobject.idle_add(idle_func)
+
+def read_stdin(): # This is the thread function
+ global hud_dict
+
+ db_connection = Database.Database(config, db_name, 'temp')
+ tourny_finder = re.compile('(\d+) (\d+)')
+
+ while True: # wait for a new hand number on stdin
+ new_hand_id = sys.stdin.readline()
+ new_hand_id = string.rstrip(new_hand_id)
+ if new_hand_id == "": # blank line means quit
+ destroy()
+
+# delete hud_dict entries for any HUD destroyed since last iteration
+ for h in hud_dict.keys():
+ if hud_dict[h].deleted:
+ del(hud_dict[h])
+
+# get basic info about the new hand from the db
+ (table_name, max, poker_game) = db_connection.get_table_name(new_hand_id)
+
+# find out if this hand is from a tournament
+ is_tournament = False
+ (tour_number, tab_number) = (0, 0)
+ mat_obj = tourny_finder.search(table_name)
+# if len(mat_obj.groups) == 2:
+ if mat_obj:
+ is_tournament = True
+ (tour_number, tab_number) = mat_obj.group(1, 2)
+
+ stat_dict = db_connection.get_stats_from_hand(new_hand_id)
+
+# if a hud for this CASH table exists, just update it
+ if hud_dict.has_key(table_name):
+# update the data for the aux_windows
+ for aw in hud_dict[table_name].aux_windows:
+ aw.update_data(new_hand_id)
+ update_HUD(new_hand_id, table_name, config, stat_dict)
+# if a hud for this TOURNAMENT table exists, just update it
+ elif hud_dict.has_key(tour_number):
+ update_HUD(new_hand_id, tour_number, config, stat_dict)
+# otherwise create a new hud
+ else:
+ if is_tournament:
+ tablewindow = Tables.discover_tournament_table(config, tour_number, tab_number)
+ if tablewindow == None:
+ sys.stderr.write("tournament %s, table %s not found\n" % (tour_number, tab_number))
+ else:
+ create_HUD(new_hand_id, tablewindow, db_name, tour_number, max, poker_game, db_connection, config, stat_dict)
+ else:
+ tablewindow = Tables.discover_table_by_name(config, table_name)
+ if tablewindow == None:
+ sys.stderr.write("table name "+table_name+" not found\n")
+ else:
+ create_HUD(new_hand_id, tablewindow, db_name, table_name, max, poker_game, db_connection, config, stat_dict)
+
+if __name__== "__main__":
+ sys.stderr.write("HUD_main starting\n")
+
+ try:
+ db_name = sys.argv[1]
+ except:
+ db_name = 'fpdb'
+ sys.stderr.write("Using db name = %s\n" % (db_name))
+
+ config = Configuration.Config()
+
+ gobject.threads_init() # this is required
+ thread.start_new_thread(read_stdin, ()) # starts the thread
+
+ main_window = gtk.Window()
+ main_window.connect("destroy", destroy)
+ eb = gtk.EventBox()
+ label = gtk.Label('Closing this window will exit from the HUD.')
+ eb.add(label)
+ main_window.add(eb)
+ main_window.set_title("HUD Main Window")
+ main_window.show_all()
+
+ gtk.main()
diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py
new file mode 100644
index 00000000..71bc0b25
--- /dev/null
+++ b/pyfpdb/HandHistoryConverter.py
@@ -0,0 +1,427 @@
+#!/usr/bin/python
+
+#Copyright 2008 Carl Gherardi
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU Affero General Public License as published by
+#the Free Software Foundation, version 3 of the License.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU Affero General Public License
+#along with this program. If not, see .
+#In the "official" distribution you can find the license in
+#agpl-3.0.txt in the docs folder of the package.
+
+import Configuration
+import FpdbRegex
+import re
+import sys
+import traceback
+import os
+import os.path
+import xml.dom.minidom
+from decimal import Decimal
+import operator
+from xml.dom.minidom import Node
+
+class HandHistoryConverter:
+ def __init__(self, config, file, sitename):
+ print "HandHistory init called"
+ self.c = config
+ self.sitename = sitename
+ self.obs = "" # One big string
+ self.filetype = "text"
+ self.doc = None # For XML based HH files
+ self.file = file
+ self.hhbase = self.c.get_import_parameters().get("hhArchiveBase")
+ self.hhbase = os.path.expanduser(self.hhbase)
+ self.hhdir = os.path.join(self.hhbase,sitename)
+ self.gametype = []
+# self.ofile = os.path.join(self.hhdir,file)
+ self.rexx = FpdbRegex.FpdbRegex()
+
+ def __str__(self):
+ tmp = "HandHistoryConverter: '%s'\n" % (self.sitename)
+ tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase)
+ tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir)
+ tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype)
+ tmp = tmp + "\tinfile: '%s'\n" % (self.file)
+# tmp = tmp + "\toutfile: '%s'\n" % (self.ofile)
+# tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
+# tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
+# tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
+# tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
+ return tmp
+
+ def processFile(self):
+ if not self.sanityCheck():
+ print "Cowardly refusing to continue after failed sanity check"
+ return
+ self.readFile(self.file)
+ self.gametype = self.determineGameType()
+ self.hands = self.splitFileIntoHands()
+ for hand in self.hands:
+ self.readHandInfo(hand)
+ self.readPlayerStacks(hand)
+ self.markStreets(hand)
+ self.readBlinds(hand)
+ self.readHeroCards(hand)
+
+ # Read action (Note: no guarantee this is in hand order.
+ for street in hand.streets.groupdict():
+ self.readAction(hand, street)
+
+ # finalise it (total the pot)
+ hand.totalPot()
+ self.getRake(hand)
+
+ if(hand.involved == True):
+ #self.writeHand("output file", hand)
+ hand.printHand()
+ else:
+ pass #Don't write out observed hands
+
+ #####
+ # These functions are parse actions that may be overridden by the inheriting class
+ #
+
+ def readSupportedGames(self): abstract
+
+ # should return a list
+ # type base limit
+ # [ ring, hold, nl , sb, bb ]
+ # Valid types specified in docs/tabledesign.html in Gametypes
+ def determineGameType(self): abstract
+
+ # Read any of:
+ # HID HandID
+ # TABLE Table name
+ # SB small blind
+ # BB big blind
+ # GAMETYPE gametype
+ # YEAR MON DAY HR MIN SEC datetime
+ # BUTTON button seat number
+ def readHandInfo(self, hand): abstract
+
+ # Needs to return a list of lists in the format
+ # [['seat#', 'player1name', 'stacksize'] ['seat#', 'player2name', 'stacksize'] [...]]
+ def readPlayerStacks(self, hand): abstract
+
+ # Needs to return a MatchObject with group names identifying the streets into the Hand object
+ # that is, pulls the chunks of preflop, flop, turn and river text into hand.streets MatchObject.
+ def markStreets(self, hand): abstract
+
+ #Needs to return a list in the format
+ # ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb,
+ # addtional players are assumed to post a bb oop
+ def readBlinds(self, hand): abstract
+ def readHeroCards(self, hand): abstract
+ def readAction(self, hand, street): abstract
+
+ # Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
+ # so that an inheriting class can calculate it for the specific site if need be.
+ def getRake(self, hand): abstract
+
+ def sanityCheck(self):
+ sane = True
+ base_w = False
+ #Check if hhbase exists and is writable
+ #Note: Will not try to create the base HH directory
+ if not (os.access(self.hhbase, os.W_OK) and os.path.isdir(self.hhbase)):
+ print "HH Sanity Check: Directory hhbase '" + self.hhbase + "' doesn't exist or is not writable"
+ else:
+ #Check if hhdir exists and is writable
+ if not os.path.isdir(self.hhdir):
+ # In first pass, dir may not exist. Attempt to create dir
+ print "Creating directory: '%s'" % (self.hhdir)
+ os.mkdir(self.hhdir)
+ sane = True
+ elif os.access(self.hhdir, os.W_OK):
+ sane = True
+ else:
+ print "HH Sanity Check: Directory hhdir '" + self.hhdir + "' or its parent directory are not writable"
+
+ return sane
+
+ # Functions not necessary to implement in sub class
+ def setFileType(self, filetype = "text"):
+ self.filetype = filetype
+
+ def splitFileIntoHands(self):
+ hands = []
+ list = self.rexx.split_hand_re.split(self.obs)
+ list.pop() #Last entry is empty
+ for l in list:
+# print "'" + l + "'"
+ hands = hands + [Hand(self.sitename, self.gametype, l)]
+ return hands
+
+ def readFile(self, filename):
+ """Read file"""
+ print "Reading file: '%s'" %(filename)
+ if(self.filetype == "text"):
+ infile=open(filename, "rU")
+ self.obs = infile.read()
+ infile.close()
+ elif(self.filetype == "xml"):
+ try:
+ doc = xml.dom.minidom.parse(filename)
+ self.doc = doc
+ except:
+ traceback.print_exc(file=sys.stderr)
+
+ def writeHand(self, file, hand):
+ """Write out parsed data"""
+ print "DEBUG: *************************"
+ print "DEBUG: Start of print hand"
+ print "DEBUG: *************************"
+
+ print "%s Game #%s: %s ($%s/$%s) - %s" %(hand.sitename, hand.handid, "XXXXhand.gametype", hand.sb, hand.bb, hand.starttime)
+ print "Table '%s' %d-max Seat #%s is the button" %(hand.tablename, hand.maxseats, hand.buttonpos)
+
+ for player in hand.players:
+ print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
+
+ if(hand.posted[0] == "FpdbNBP"):
+ print "No small blind posted"
+ else:
+ print "%s: posts small blind $%s" %(hand.posted[0], hand.sb)
+
+ #May be more than 1 bb posting
+ print "%s: posts big blind $%s" %(hand.posted[1], hand.bb)
+ if(len(hand.posted) > 2):
+ # Need to loop on all remaining big blinds - lazy
+ print "XXXXXXXXX FIXME XXXXXXXX"
+
+ print "*** HOLE CARDS ***"
+ print "Dealt to %s [%s %s]" %(hand.hero , hand.holecards[0], hand.holecards[1])
+#
+## ACTION STUFF
+# This is no limit only at the moment
+
+ for act in hand.actions['PREFLOP']:
+ self.printActionLine(act, 0)
+
+ if 'PREFLOP' in hand.actions:
+ for act in hand.actions['PREFLOP']:
+ print "PF action"
+
+ if 'FLOP' in hand.actions:
+ print "*** FLOP *** [%s %s %s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"))
+ for act in hand.actions['FLOP']:
+ self.printActionLine(act, 0)
+
+ if 'TURN' in hand.actions:
+ print "*** TURN *** [%s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"))
+ for act in hand.actions['TURN']:
+ self.printActionLine(act, 0)
+
+ if 'RIVER' in hand.actions:
+ print "*** RIVER *** [%s %s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"), hand.streets.group("RIVER1"))
+ for act in hand.actions['RIVER']:
+ self.printActionLine(act, 0)
+
+ print "*** SUMMARY ***"
+ print "XXXXXXXXXXXX Need sumary info XXXXXXXXXXX"
+# print "Total pot $%s | Rake $%s)" %(hand.totalpot $" + hand.rake)
+# print "Board [" + boardcards + "]"
+#
+# SUMMARY STUFF
+
+
+ def printActionLine(self, act, pot):
+ if act[1] == 'folds' or act[1] == 'checks':
+ print "%s: %s " %(act[0], act[1])
+ if act[1] == 'calls':
+ print "%s: %s $%s" %(act[0], act[1], act[2])
+ if act[1] == 'raises':
+ print "%s: %s $%s to XXXpottotalXXX" %(act[0], act[1], act[2])
+
+
+#takes a poker float (including , for thousand seperator and converts it to an int
+ def float2int (self, string):
+ pos=string.find(",")
+ if (pos!=-1): #remove , the thousand seperator
+ string=string[0:pos]+string[pos+1:]
+
+ pos=string.find(".")
+ if (pos!=-1): #remove decimal point
+ string=string[0:pos]+string[pos+1:]
+
+ result = int(string)
+ if pos==-1: #no decimal point - was in full dollars - need to multiply with 100
+ result*=100
+ return result
+#end def float2int
+
+class Hand:
+# def __init__(self, sitename, gametype, sb, bb, string):
+
+ UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'}
+ STREETS = ['BLINDS','PREFLOP','FLOP','TURN','RIVER']
+ def __init__(self, sitename, gametype, string):
+ self.sitename = sitename
+ self.gametype = gametype
+ self.string = string
+
+ self.streets = None # A MatchObject using a groupnames to identify streets.
+ self.actions = {}
+
+ self.handid = 0
+ self.sb = gametype[3]
+ self.bb = gametype[4]
+ self.tablename = "Slartibartfast"
+ self.maxseats = 10
+ self.counted_seats = 0
+ self.buttonpos = 0
+ self.seating = []
+ self.players = []
+ self.posted = []
+ self.involved = True
+ self.hero = "Hiro"
+ self.holecards = "Xx Xx"
+ self.action = []
+ self.totalpot = None
+ self.rake = None
+
+ self.bets = {}
+ self.lastBet = {}
+ for street in self.STREETS:
+ self.bets[street] = {}
+ self.lastBet[street] = 0
+
+ def addPlayer(self, seat, name, chips):
+ """seat, an int indicating the seat
+ name, the player name
+ chips, the chips the player has at the start of the hand"""
+ #self.players.append(name)
+ self.players.append([seat, name, chips])
+ #self.startChips[name] = chips
+ #self.endChips[name] = chips
+ #self.winners[name] = 0
+ for street in self.STREETS:
+ self.bets[street][name] = []
+
+
+ def addHoleCards(self,h1,h2,seat=None): # generalise to add hole cards for a specific seat or player
+ self.holecards = [self.card(h1), self.card(h2)]
+
+
+ def card(self,c):
+ """upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
+ # don't know how to make this 'static'
+ for k,v in self.UPS.items():
+ c = c.replace(k,v)
+ return c
+
+ def addBlind(self, player, amount):
+ # if player is None, it's a missing small blind.
+ if player is not None:
+ self.bets['PREFLOP'][player].append(Decimal(amount))
+ self.lastBet['PREFLOP'] = Decimal(amount)
+ self.posted += [player]
+
+
+ def addCall(self, street, player=None, amount=None):
+ # Potentially calculate the amount of the call if not supplied
+ # corner cases include if player would be all in
+ if amount is not None:
+ self.bets[street][player].append(Decimal(amount))
+ #self.lastBet[street] = Decimal(amount)
+ self.actions[street] += [[player, 'calls', amount]]
+
+ def addRaiseTo(self, street, player, amountTo):
+ # Given only the amount raised to, the amount of the raise can be calculated by
+ # working out how much this player has already in the pot
+ # (which is the sum of self.bets[street][player])
+ # and how much he needs to call to match the previous player
+ # (which is tracked by self.lastBet)
+ committedThisStreet = reduce(operator.add, self.bets[street][player], 0)
+ amountToCall = self.lastBet[street] - committedThisStreet
+ self.lastBet[street] = Decimal(amountTo)
+ amountBy = Decimal(amountTo) - amountToCall
+ self.bets[street][player].append(amountBy+amountToCall)
+ self.actions[street] += [[player, 'raises', amountBy, amountTo]]
+
+ def addBet(self, street, player=None, amount=0):
+ self.bets[street][name].append(Decimal(amount))
+ self.orderedBets[street].append(Decimal(amount))
+ self.actions[street] += [[player, 'bets', amount]]
+
+ def totalPot(self):
+
+ if self.totalpot is None:
+ self.totalpot = 0
+
+ # player names:
+ # print [x[1] for x in self.players]
+ for player in [x[1] for x in self.players]:
+ for street in self.STREETS:
+ print street, self.bets[street][player]
+ self.totalpot += reduce(operator.add, self.bets[street][player], 0)
+
+
+
+ def printHand(self):
+ # PokerStars format.
+ print "### DEBUG ###"
+ print "%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, "XXXXhand.gametype", self.sb, self.bb, self.starttime)
+ print "Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)
+ for player in self.players:
+ print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
+
+ if(self.posted[0] is None):
+ print "No small blind posted"
+ else:
+ print "%s: posts small blind $%s" %(self.posted[0], self.sb)
+
+ #May be more than 1 bb posting
+ for a in self.posted[1:]:
+ print "%s: posts big blind $%s" %(self.posted[1], self.bb)
+
+ # What about big & small blinds?
+
+ print "*** HOLE CARDS ***"
+ print "Dealt to %s [%s %s]" %(self.hero , self.holecards[0], self.holecards[1])
+
+ if 'PREFLOP' in self.actions:
+ for act in self.actions['PREFLOP']:
+ self.printActionLine(act)
+
+ if 'FLOP' in self.actions:
+ print "*** FLOP *** [%s %s %s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"))
+ for act in self.actions['FLOP']:
+ self.printActionLine(act)
+
+ if 'TURN' in self.actions:
+ print "*** TURN *** [%s %s %s] [%s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"))
+ for act in self.actions['TURN']:
+ self.printActionLine(act)
+
+ if 'RIVER' in self.actions:
+ print "*** RIVER *** [%s %s %s %s] [%s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"), self.streets.group("RIVER1"))
+ for act in self.actions['RIVER']:
+ self.printActionLine(act)
+
+
+ #Some sites don't have a showdown section so we have to figure out if there should be one
+ # The logic for a showdown is: at the end of river action there are at least two players in the hand
+ if 'SHOWDOWN' in self.actions:
+ print "*** SHOW DOWN ***"
+ print "what do they show"
+
+ print "*** SUMMARY ***"
+ print "Total pot $%s | Rake $%s)" % (self.totalpot, self.rake)
+ print "Board [%s %s %s %s %s]" % (self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"), self.streets.group("RIVER1"))
+
+
+ def printActionLine(self, act):
+ if act[1] == 'folds' or act[1] == 'checks':
+ print "%s: %s " %(act[0], act[1])
+ if act[1] == 'calls':
+ print "%s: %s $%s" %(act[0], act[1], act[2])
+ if act[1] == 'raises':
+ print "%s: %s $%s to $%s" %(act[0], act[1], act[2], act[3])
\ No newline at end of file
diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py
index 1ad38ee0..80ec9a82 100644
--- a/pyfpdb/Hud.py
+++ b/pyfpdb/Hud.py
@@ -1,578 +1,572 @@
-#!/usr/bin/env python
-"""Hud.py
-
-Create and manage the hud overlays.
-"""
-# Copyright 2008, 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
-
-# pyGTK modules
-import pygtk
-import gtk
-import pango
-import gobject
-
-# win32 modules -- only imported on windows systems
-if os.name == 'nt':
- import win32gui
- import win32con
- import win32api
-
-# FreePokerTools modules
-import Tables # needed for testing only
-import Configuration
-import Stats
-import Mucked
-import Database
-import HUD_main
-
-class Hud:
-
- def __init__(self, table, max, poker_game, config, db_name):
- self.table = table
- self.config = config
- self.poker_game = poker_game
- self.max = max
- self.db_name = db_name
- self.deleted = False
- self.stacked = True
- self.colors = config.get_default_colors(self.table.site)
-
- self.stat_windows = {}
- self.popup_windows = {}
- self.aux_windows = []
- self.font = pango.FontDescription("Sans 8")
-
-# Set up a main window for this this instance of the HUD
- self.main_window = gtk.Window()
-# self.window.set_decorated(0)
- self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC)
- self.main_window.set_title(table.name + " FPDBHUD")
- self.main_window.connect("destroy", self.kill_hud)
- self.main_window.set_decorated(False)
- self.main_window.set_opacity(self.colors["hudopacity"])
- #self.main_window.set_transient_for(parent.get_toplevel())
-
- self.ebox = gtk.EventBox()
-# self.label = gtk.Label("Right click to close HUD for %s\nor Save Stat Positions." % (table.name))
- self.label = gtk.Label("FPDB Menu (Right Click)\nLeft-drag to move")
-
- self.label.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor']))
- self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
-
- self.main_window.add(self.ebox)
- self.ebox.add(self.label)
- self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor']))
- self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
-
- self.main_window.move(self.table.x, self.table.y)
-
-# A popup window for the main window
- self.menu = gtk.Menu()
- self.item1 = gtk.MenuItem('Kill this HUD')
- self.menu.append(self.item1)
- self.item1.connect("activate", self.kill_hud)
- self.item1.show()
- self.item2 = gtk.MenuItem('Save Layout')
- self.menu.append(self.item2)
- self.item2.connect("activate", self.save_layout)
- self.item2.show()
- self.item3 = gtk.MenuItem('Reposition Stats')
- self.menu.append(self.item3)
- self.item3.connect("activate", self.reposition_windows)
- self.item3.show()
- self.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
-
- self.main_window.show_all()
-# set_keep_above(1) for windows
- if os.name == 'nt':
- self.topify_window(self.main_window)
- else:
- self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(self.table.number) # gets a gdk handle for poker client
- self.main_window.gdkhandle = gtk.gdk.window_foreign_new(self.main_window.window.xid) # gets a gdk handle for the hud table window
- self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) #
-
- self.main_window.set_destroy_with_parent(True)
-
- def on_button_press(self, widget, event):
- if event.button == 1:
- self.main_window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
- return True
- if event.button == 3:
- widget.popup(None, None, None, event.button, event.time)
- return True
- return False
-
- def kill_hud(self, *args):
- for k in self.stat_windows.keys():
- self.stat_windows[k].window.destroy()
- self.main_window.destroy()
- self.deleted = True
-
- def reposition_windows(self, *args):
- for w in self.stat_windows:
- self.stat_windows[w].window.move(self.stat_windows[w].x,
- self.stat_windows[w].y)
- def save_layout(self, *args):
- new_layout = [(0, 0)] * self.max
-# todo: have the hud track the poker table's window position regularly, don't forget to update table.x and table.y.
- for sw in self.stat_windows:
- loc = self.stat_windows[sw].window.get_position()
- new_loc = (loc[0] - self.table.x, loc[1] - self.table.y)
- new_layout[self.stat_windows[sw].adj - 1] = new_loc
- self.config.edit_layout(self.table.site, self.max, locations = new_layout)
- self.config.save()
-
- def adj_seats(self, hand, config):
- adj = range(0, self.max + 1) # default seat adjustments = no adjustment
-# does the user have a fav_seat?
- try:
- if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
- fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
- db_connection = Database.Database(config, self.db_name, 'temp')
- actual_seat = db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name)
- db_connection.close_connection()
- for i in range(0, self.max + 1):
- j = actual_seat + i
- if j > self.max: j = j - self.max
- adj[j] = fav_seat + i
- if adj[j] > self.max: adj[j] = adj[j] - self.max
- except:
- pass
- return adj
-
- def create(self, hand, config):
-# update this hud, to the stats and players as of "hand"
-# hand is the hand id of the most recent hand played at this table
-#
-# this method also manages the creating and destruction of stat
-# windows via calls to the Stat_Window class
-
- adj = self.adj_seats(hand, config)
- loc = self.config.get_locations(self.table.site, self.max)
-
-# create the stat windows
- for i in range(1, self.max + 1):
- (x, y) = loc[adj[i]]
- if self.stat_windows.has_key(i):
- self.stat_windows[i].relocate(x, y)
- else:
- self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game],
- parent = self,
- table = self.table,
- x = x,
- y = y,
- seat = i,
- adj = adj[i],
- player_id = 'fake',
- font = self.font)
-
- self.stats = []
- for i in range(0, config.supported_games[self.poker_game].rows + 1):
- row_list = [''] * config.supported_games[self.poker_game].cols
- self.stats.append(row_list)
- for stat in config.supported_games[self.poker_game].stats.keys():
- self.stats[config.supported_games[self.poker_game].stats[stat].row] \
- [config.supported_games[self.poker_game].stats[stat].col] = \
- config.supported_games[self.poker_game].stats[stat].stat_name
-
- game_params = config.get_game_parameters(self.poker_game)
- if not game_params['aux'] == "":
- aux_params = config.get_aux_parameters(game_params['aux'])
- self.aux_windows.append(eval("%s.%s(gtk.Window(), config, 'fpdb')" % (aux_params['module'], aux_params['class'])))
-
- def update(self, hand, config, stat_dict):
- self.hand = hand # this is the last hand, so it is available later
- for s in stat_dict.keys():
- try:
- self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id']
- except: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here
- self.max = 10
- self.create(hand, config)
- self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id']
-
- for r in range(0, config.supported_games[self.poker_game].rows):
- for c in range(0, config.supported_games[self.poker_game].cols):
- this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]]
- number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c])
- statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix
- self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(statstring)
- tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \
- number[3] + ", " + number[4]
- Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip)
-# for m in self.aux_windows:
-# m.update_data(hand)
-# m.update_gui(hand)
-
- def topify_window(self, window):
- """Set the specified gtk window to stayontop in MS Windows."""
-
- def windowEnumerationHandler(hwnd, resultList):
- '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
- resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
-
- unique_name = 'unique name for finding this window'
- real_name = window.get_title()
- window.set_title(unique_name)
- tl_windows = []
- win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
-
- for w in tl_windows:
- if w[1] == unique_name:
- #win32gui.ShowWindow(w[0], win32con.SW_HIDE)
- window.parentgdkhandle = gtk.gdk.window_foreign_new(long(self.table.number))
- self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
- self.main_window.gdkhandle.set_transient_for(window.parentgdkhandle)
- #win32gui.ShowWindow(w[0], win32con.SW_SHOW)
-
- style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
- #style |= win32con.WS_EX_TOOLWINDOW
- #style &= ~win32con.WS_EX_APPWINDOW
- style |= win32con.WS_CLIPCHILDREN
- win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style)
-
-
- #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
-
-# notify_id = (w[0],
-# 0,
-# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
-# win32con.WM_USER+20,
-# 0,
-# '')
-# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id)
-#
- window.set_title(real_name)
-
-class Stat_Window:
-
- def button_press_cb(self, widget, event, *args):
-# This handles all callbacks from button presses on the event boxes in
-# the stat windows. There is a bit of an ugly kludge to separate single-
-# and double-clicks.
-
- if event.button == 3: # right button event
- if event.type == gtk.gdk.BUTTON_PRESS: # left button single click
- if self.sb_click > 0: return
- self.sb_click = gobject.timeout_add(250, self.single_click, widget)
- elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click
- if self.sb_click > 0:
- gobject.source_remove(self.sb_click)
- self.sb_click = 0
- self.double_click(widget, event, *args)
-
- if event.button == 2: # middle button event
-# print "middle button clicked"
- pass
-
- if event.button == 1: # left button event
- if event.state & gtk.gdk.SHIFT_MASK:
- self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time)
- else:
- self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
-
- def single_click(self, widget):
-# Callback from the timeout in the single-click finding part of the
-# button press call back. This needs to be modified to get all the
-# arguments from the call.
-# print "left button clicked"
- self.sb_click = 0
- Popup_window(widget, self)
- return False
-
- def double_click(self, widget, event, *args):
- self.toggle_decorated(widget)
-
- def toggle_decorated(self, widget):
- top = widget.get_toplevel()
- (x, y) = top.get_position()
-
- if top.get_decorated():
- top.set_decorated(0)
- top.move(x, y)
- else:
- top.set_decorated(1)
- top.move(x, y)
-
- def relocate(self, x, y):
- self.x = x + self.table.x
- self.y = y + self.table.y
- self.window.move(self.x, self.y)
-
- def __init__(self, parent, game, table, seat, adj, x, y, player_id, font):
- self.parent = parent # Hud object that this stat window belongs to
- self.game = game # Configuration object for the curren
- self.table = table # Table object where this is going
- self.seat = seat # seat number of his player
- self.adj = adj # the adjusted seat number for this player
- self.x = x + table.x # table.x and y are the location of the table
- self.y = y + table.y # x and y are the location relative to table.x & y
- self.player_id = player_id # looks like this isn't used ;)
- self.sb_click = 0 # used to figure out button clicks
-
- self.window = gtk.Window()
- self.window.set_decorated(0)
- self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
-
- self.window.set_title("%s" % seat)
- self.window.set_property("skip-taskbar-hint", True)
- self.window.set_transient_for(parent.main_window)
-
- self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False)
- self.window.add(self.grid)
-
- self.e_box = []
- self.frame = []
- self.label = []
- for r in range(self.game.rows):
- self.e_box.append([])
- self.label.append([])
- for c in range(self.game.cols):
- self.e_box[r].append( gtk.EventBox() )
-
- self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor']))
- self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor']))
-
- Stats.do_tip(self.e_box[r][c], 'farts')
- self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
- self.label[r].append( gtk.Label('xxx') )
-
- self.label[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor']))
- self.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor']))
-
- self.e_box[r][c].add(self.label[r][c])
- self.e_box[r][c].connect("button_press_event", self.button_press_cb)
-# font = pango.FontDescription("Sans 8")
- self.label[r][c].modify_font(font)
-
-# if not os.name == 'nt': # seems to be a bug in opacity on windows
- self.window.set_opacity(parent.colors['hudopacity'])
-
- self.window.realize
- self.window.move(self.x, self.y)
- self.window.show_all()
-# set_keep_above(1) for windows
- if os.name == 'nt': self.topify_window(self.window)
-
- def topify_window(self, window):
- """Set the specified gtk window to stayontop in MS Windows."""
-
- def windowEnumerationHandler(hwnd, resultList):
- '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
- resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
-
- unique_name = 'unique name for finding this window'
- real_name = window.get_title()
- window.set_title(unique_name)
- tl_windows = []
- win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
-
- for w in tl_windows:
- if w[1] == unique_name:
-
- #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
-
-# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE)
-# style |= win32con.WS_EX_TOOLWINDOW
-# style &= ~win32con.WS_EX_APPWINDOW
-# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style)
- win32gui.ShowWindow(w[0], win32con.SW_SHOW)
- window.set_title(real_name)
-
-def destroy(*args): # call back for terminating the main eventloop
- gtk.main_quit()
-
-class Popup_window:
- def __init__(self, parent, stat_window):
- self.sb_click = 0
-
-# create the popup window
- self.window = gtk.Window()
- self.window.set_decorated(0)
- self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
-# self.window.set_keep_above(1)
- self.window.set_title("popup")
- self.window.set_property("skip-taskbar-hint", True)
- self.window.set_transient_for(parent.get_toplevel())
-
- self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
-
- self.ebox = gtk.EventBox()
- self.ebox.connect("button_press_event", self.button_press_cb)
- self.lab = gtk.Label("stuff\nstuff\nstuff")
-
-# need an event box so we can respond to clicks
- self.window.add(self.ebox)
- self.ebox.add(self.lab)
-
- self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
- self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
- self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
- self.window.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
- self.lab.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
- self.lab.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
-
- self.window.realize
-
-# figure out the row, col address of the click that activated the popup
- row = 0
- col = 0
- for r in range(0, stat_window.game.rows):
- for c in range(0, stat_window.game.cols):
- if stat_window.e_box[r][c] == parent:
- row = r
- col = c
- break
-
-# figure out what popup format we're using
- popup_format = "default"
- for stat in stat_window.game.stats.keys():
- if stat_window.game.stats[stat].row == row and stat_window.game.stats[stat].col == col:
- popup_format = stat_window.game.stats[stat].popup
- break
-
-# get the list of stats to be presented from the config
- stat_list = []
- for w in stat_window.parent.config.popup_windows.keys():
- if w == popup_format:
- stat_list = stat_window.parent.config.popup_windows[w].pu_stats
- break
-
-# get a database connection
- db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp')
-
-# calculate the stat_dict and then create the text for the pu
-# stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id)
- stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand)
- db_connection.close_connection()
-
- pu_text = ""
- for s in stat_list:
- number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s)
- pu_text += number[3] + "\n"
-
- self.lab.set_text(pu_text)
- self.window.show_all()
-
- self.window.set_transient_for(stat_window.window)
-
-# set_keep_above(1) for windows
- if os.name == 'nt': self.topify_window(self.window)
-
- def button_press_cb(self, widget, event, *args):
-# This handles all callbacks from button presses on the event boxes in
-# the popup windows. There is a bit of an ugly kludge to separate single-
-# and double-clicks. This is the same code as in the Stat_window class
- if event.button == 1: # left button event
- if event.type == gtk.gdk.BUTTON_PRESS: # left button single click
- if self.sb_click > 0: return
- self.sb_click = gobject.timeout_add(250, self.single_click, widget)
- elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click
- if self.sb_click > 0:
- gobject.source_remove(self.sb_click)
- self.sb_click = 0
- self.double_click(widget, event, *args)
-
- if event.button == 2: # middle button event
- pass
-# print "middle button clicked"
-
- if event.button == 3: # right button event
- pass
-# print "right button clicked"
-
- def single_click(self, widget):
-# Callback from the timeout in the single-click finding part of the
-# button press call back. This needs to be modified to get all the
-# arguments from the call.
- self.sb_click = 0
- self.window.destroy()
- return False
-
- def double_click(self, widget, event, *args):
- self.toggle_decorated(widget)
-
- def toggle_decorated(self, widget):
- top = widget.get_toplevel()
- (x, y) = top.get_position()
-
- if top.get_decorated():
- top.set_decorated(0)
- top.move(x, y)
- else:
- top.set_decorated(1)
- top.move(x, y)
-
- def topify_window(self, window):
- """Set the specified gtk window to stayontop in MS Windows."""
-
- def windowEnumerationHandler(hwnd, resultList):
- '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
- resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
-
- unique_name = 'unique name for finding this window'
- real_name = window.get_title()
- window.set_title(unique_name)
- tl_windows = []
- win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
-
- for w in tl_windows:
- if w[1] == unique_name:
-# win32gui.ShowWindow(w[0], win32con.SW_HIDE)
-# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE)
-# style |= win32con.WS_EX_TOOLWINDOW
-# style &= ~win32con.WS_EX_APPWINDOW
-# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style)
-# win32gui.ShowWindow(w[0], win32con.SW_SHOW)
- win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
-
-# notify_id = (w[0],
-# 0,
-# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
-# win32con.WM_USER+20,
-# 0,
-# '')
-# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id)
-#
- window.set_title(real_name)
-
-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, "Chelsea")
- if t is None:
- print "Table not found."
- db = Database.Database(c, 'fpdb', 'holdem')
-
-# for t in tables:
- win = Hud(t, 10, 'holdem', c, db)
- win.create(1, c)
-# t.get_details()
- win.update(8300, db, c)
-
- gtk.main()
+#!/usr/bin/env python
+"""Hud.py
+
+Create and manage the hud overlays.
+"""
+# Copyright 2008, 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
+
+# pyGTK modules
+import pygtk
+import gtk
+import pango
+import gobject
+
+# win32 modules -- only imported on windows systems
+if os.name == 'nt':
+ import win32gui
+ import win32con
+ import win32api
+
+# FreePokerTools modules
+import Tables # needed for testing only
+import Configuration
+import Stats
+import Mucked
+import Database
+import HUD_main
+
+class Hud:
+
+ def __init__(self, table, max, poker_game, config, db_name):
+ self.table = table
+ self.config = config
+ self.poker_game = poker_game
+ self.max = max
+ self.db_name = db_name
+ self.deleted = False
+ self.stacked = True
+ self.colors = config.get_default_colors(self.table.site)
+
+ self.stat_windows = {}
+ self.popup_windows = {}
+ self.aux_windows = []
+ self.font = pango.FontDescription("Sans 7")
+
+# Set up a main window for this this instance of the HUD
+ self.main_window = gtk.Window()
+# self.window.set_decorated(0)
+ self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC)
+ self.main_window.set_title(table.name + " FPDBHUD")
+ self.main_window.connect("destroy", self.kill_hud)
+ self.main_window.set_decorated(False)
+ self.main_window.set_opacity(self.colors["hudopacity"])
+ #self.main_window.set_transient_for(parent.get_toplevel())
+
+ self.ebox = gtk.EventBox()
+# self.label = gtk.Label("Right click to close HUD for %s\nor Save Stat Positions." % (table.name))
+ self.label = gtk.Label("FPDB Menu (Right Click)\nLeft-drag to move")
+
+ self.label.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor']))
+ self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
+
+ self.main_window.add(self.ebox)
+ self.ebox.add(self.label)
+ self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor']))
+ self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
+
+ self.main_window.move(self.table.x, self.table.y)
+
+# A popup window for the main window
+ self.menu = gtk.Menu()
+ self.item1 = gtk.MenuItem('Kill this HUD')
+ self.menu.append(self.item1)
+ self.item1.connect("activate", self.kill_hud)
+ self.item1.show()
+
+ self.item2 = gtk.MenuItem('Save Layout')
+ self.menu.append(self.item2)
+ self.item2.connect("activate", self.save_layout)
+ self.item2.show()
+
+ self.item3 = gtk.MenuItem('Reposition Stats')
+ self.menu.append(self.item3)
+ self.item3.connect("activate", self.reposition_windows)
+ self.item3.show()
+
+ self.item4 = gtk.MenuItem('Debug Stat Windows')
+ self.menu.append(self.item4)
+ self.item4.connect("activate", self.debug_stat_windows)
+ self.item4.show()
+
+ self.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
+
+ self.main_window.show_all()
+# set_keep_above(1) for windows
+ if os.name == 'nt':
+ self.topify_window(self.main_window)
+ else:
+ self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(self.table.number) # gets a gdk handle for poker client
+ self.main_window.gdkhandle = gtk.gdk.window_foreign_new(self.main_window.window.xid) # gets a gdk handle for the hud table window
+ self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) #
+
+ self.main_window.set_destroy_with_parent(True)
+
+ def on_button_press(self, widget, event):
+ if event.button == 1:
+ self.main_window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
+ return True
+ if event.button == 3:
+ widget.popup(None, None, None, event.button, event.time)
+ return True
+ return False
+
+ def kill_hud(self, *args):
+ for k in self.stat_windows.keys():
+ self.stat_windows[k].window.destroy()
+ self.main_window.destroy()
+ self.deleted = True
+
+ def reposition_windows(self, *args):
+ for w in self.stat_windows:
+ self.stat_windows[w].window.move(self.stat_windows[w].x,
+ self.stat_windows[w].y)
+
+ def debug_stat_windows(self, *args):
+ print self.table, "\n", self.main_window.window.get_transient_for()
+ for w in self.stat_windows:
+ print self.stat_windows[w].window.window.get_transient_for()
+
+ def save_layout(self, *args):
+ new_layout = [(0, 0)] * self.max
+# todo: have the hud track the poker table's window position regularly, don't forget to update table.x and table.y.
+ for sw in self.stat_windows:
+ loc = self.stat_windows[sw].window.get_position()
+ new_loc = (loc[0] - self.table.x, loc[1] - self.table.y)
+ new_layout[self.stat_windows[sw].adj - 1] = new_loc
+ self.config.edit_layout(self.table.site, self.max, locations = new_layout)
+ self.config.save()
+
+ def adj_seats(self, hand, config):
+ adj = range(0, self.max + 1) # default seat adjustments = no adjustment
+# does the user have a fav_seat?
+ try:
+ if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
+ fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
+ db_connection = Database.Database(config, self.db_name, 'temp')
+ actual_seat = db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name)
+ db_connection.close_connection()
+ for i in range(0, self.max + 1):
+ j = actual_seat + i
+ if j > self.max: j = j - self.max
+ adj[j] = fav_seat + i
+ if adj[j] > self.max: adj[j] = adj[j] - self.max
+ except:
+ pass
+ return adj
+
+ def create(self, hand, config):
+# update this hud, to the stats and players as of "hand"
+# hand is the hand id of the most recent hand played at this table
+#
+# this method also manages the creating and destruction of stat
+# windows via calls to the Stat_Window class
+
+ adj = self.adj_seats(hand, config)
+ loc = self.config.get_locations(self.table.site, self.max)
+
+# create the stat windows
+ for i in range(1, self.max + 1):
+ (x, y) = loc[adj[i]]
+ if self.stat_windows.has_key(i):
+ self.stat_windows[i].relocate(x, y)
+ else:
+ self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game],
+ parent = self,
+ table = self.table,
+ x = x,
+ y = y,
+ seat = i,
+ adj = adj[i],
+ player_id = 'fake',
+ font = self.font)
+
+ self.stats = []
+ for i in range(0, config.supported_games[self.poker_game].rows + 1):
+ row_list = [''] * config.supported_games[self.poker_game].cols
+ self.stats.append(row_list)
+ for stat in config.supported_games[self.poker_game].stats.keys():
+ self.stats[config.supported_games[self.poker_game].stats[stat].row] \
+ [config.supported_games[self.poker_game].stats[stat].col] = \
+ config.supported_games[self.poker_game].stats[stat].stat_name
+
+ game_params = config.get_game_parameters(self.poker_game)
+ if not game_params['aux'] == "":
+ aux_params = config.get_aux_parameters(game_params['aux'])
+ self.aux_windows.append(eval("%s.%s(gtk.Window(), config, 'fpdb')" % (aux_params['module'], aux_params['class'])))
+
+ def update(self, hand, config, stat_dict):
+ self.hand = hand # this is the last hand, so it is available later
+ for s in stat_dict.keys():
+ try:
+ self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id']
+ except: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here
+ self.max = 10
+ self.create(hand, config)
+ self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id']
+
+ for r in range(0, config.supported_games[self.poker_game].rows):
+ for c in range(0, config.supported_games[self.poker_game].cols):
+ this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]]
+ number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c])
+ statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix
+
+ if this_stat.hudcolor != "":
+ self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
+ self.stat_windows[stat_dict[s]['seat']].label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor))
+
+ self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(statstring)
+ if statstring != "xxx":
+ self.stat_windows[stat_dict[s]['seat']].window.show_all()
+ tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \
+ number[3] + ", " + number[4]
+ Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip)
+# for m in self.aux_windows:
+# m.update_data(hand)
+# m.update_gui(hand)
+
+ def topify_window(self, window):
+ """Set the specified gtk window to stayontop in MS Windows."""
+
+ def windowEnumerationHandler(hwnd, resultList):
+ '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
+ resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
+
+ unique_name = 'unique name for finding this window'
+ real_name = window.get_title()
+ window.set_title(unique_name)
+ tl_windows = []
+ win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
+
+ for w in tl_windows:
+ if w[1] == unique_name:
+ #win32gui.ShowWindow(w[0], win32con.SW_HIDE)
+ window.parentgdkhandle = gtk.gdk.window_foreign_new(long(self.table.number))
+ self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
+ self.main_window.gdkhandle.set_transient_for(window.parentgdkhandle)
+ #win32gui.ShowWindow(w[0], win32con.SW_SHOW)
+
+ style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
+ #style |= win32con.WS_EX_TOOLWINDOW
+ #style &= ~win32con.WS_EX_APPWINDOW
+ style |= win32con.WS_CLIPCHILDREN
+ win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style)
+
+
+ #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
+
+# notify_id = (w[0],
+# 0,
+# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
+# win32con.WM_USER+20,
+# 0,
+# '')
+# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id)
+#
+ window.set_title(real_name)
+
+class Stat_Window:
+
+ def button_press_cb(self, widget, event, *args):
+# This handles all callbacks from button presses on the event boxes in
+# the stat windows. There is a bit of an ugly kludge to separate single-
+# and double-clicks.
+
+ if event.button == 3: # right button event
+ if event.type == gtk.gdk.BUTTON_PRESS: # left button single click
+ if self.sb_click > 0: return
+ self.sb_click = gobject.timeout_add(250, self.single_click, widget)
+ elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click
+ if self.sb_click > 0:
+ gobject.source_remove(self.sb_click)
+ self.sb_click = 0
+ self.double_click(widget, event, *args)
+
+ if event.button == 2: # middle button event
+ self.window.hide()
+# print "middle button clicked"
+ pass
+
+ if event.button == 1: # left button event
+ if event.state & gtk.gdk.SHIFT_MASK:
+ self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time)
+ else:
+ self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
+
+ def single_click(self, widget):
+# Callback from the timeout in the single-click finding part of the
+# button press call back. This needs to be modified to get all the
+# arguments from the call.
+# print "left button clicked"
+ self.sb_click = 0
+ Popup_window(widget, self)
+ return False
+
+ def double_click(self, widget, event, *args):
+ self.toggle_decorated(widget)
+
+ def toggle_decorated(self, widget):
+ top = widget.get_toplevel()
+ (x, y) = top.get_position()
+
+ if top.get_decorated():
+ top.set_decorated(0)
+ top.move(x, y)
+ else:
+ top.set_decorated(1)
+ top.move(x, y)
+
+ def relocate(self, x, y):
+ self.x = x + self.table.x
+ self.y = y + self.table.y
+ self.window.move(self.x, self.y)
+
+ def __init__(self, parent, game, table, seat, adj, x, y, player_id, font):
+ self.parent = parent # Hud object that this stat window belongs to
+ self.game = game # Configuration object for the curren
+ self.table = table # Table object where this is going
+ self.seat = seat # seat number of his player
+ self.adj = adj # the adjusted seat number for this player
+ self.x = x + table.x # table.x and y are the location of the table
+ self.y = y + table.y # x and y are the location relative to table.x & y
+ self.player_id = player_id # looks like this isn't used ;)
+ self.sb_click = 0 # used to figure out button clicks
+
+ self.window = gtk.Window()
+ self.window.set_decorated(0)
+ self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
+
+ self.window.set_title("%s" % seat)
+ self.window.set_property("skip-taskbar-hint", True)
+ self.window.set_transient_for(parent.main_window)
+
+ self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False)
+ self.window.add(self.grid)
+
+ self.e_box = []
+ self.frame = []
+ self.label = []
+ for r in range(self.game.rows):
+ self.e_box.append([])
+ self.label.append([])
+ for c in range(self.game.cols):
+ self.e_box[r].append( gtk.EventBox() )
+
+ self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor']))
+ self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor']))
+
+ Stats.do_tip(self.e_box[r][c], 'farts')
+ self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
+ self.label[r].append( gtk.Label('xxx') )
+
+ self.label[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor']))
+ self.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor']))
+
+ self.e_box[r][c].add(self.label[r][c])
+ self.e_box[r][c].connect("button_press_event", self.button_press_cb)
+ font = pango.FontDescription("Sans 7")
+ self.label[r][c].modify_font(font)
+
+ self.window.set_opacity(parent.colors['hudopacity'])
+
+ self.window.move(self.x, self.y)
+
+ self.window.hide()
+
+def destroy(*args): # call back for terminating the main eventloop
+ gtk.main_quit()
+
+class Popup_window:
+ def __init__(self, parent, stat_window):
+ self.sb_click = 0
+
+# create the popup window
+ self.window = gtk.Window()
+ self.window.set_decorated(0)
+ self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
+# self.window.set_keep_above(1)
+ self.window.set_title("popup")
+ self.window.set_property("skip-taskbar-hint", True)
+ self.window.set_transient_for(parent.get_toplevel())
+
+ self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
+
+ self.ebox = gtk.EventBox()
+ self.ebox.connect("button_press_event", self.button_press_cb)
+ self.lab = gtk.Label("stuff\nstuff\nstuff")
+
+# need an event box so we can respond to clicks
+ self.window.add(self.ebox)
+ self.ebox.add(self.lab)
+
+ self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
+ self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
+ self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
+ self.window.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
+ self.lab.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
+ self.lab.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
+
+# self.window.realize()
+
+# figure out the row, col address of the click that activated the popup
+ row = 0
+ col = 0
+ for r in range(0, stat_window.game.rows):
+ for c in range(0, stat_window.game.cols):
+ if stat_window.e_box[r][c] == parent:
+ row = r
+ col = c
+ break
+
+# figure out what popup format we're using
+ popup_format = "default"
+ for stat in stat_window.game.stats.keys():
+ if stat_window.game.stats[stat].row == row and stat_window.game.stats[stat].col == col:
+ popup_format = stat_window.game.stats[stat].popup
+ break
+
+# get the list of stats to be presented from the config
+ stat_list = []
+ for w in stat_window.parent.config.popup_windows.keys():
+ if w == popup_format:
+ stat_list = stat_window.parent.config.popup_windows[w].pu_stats
+ break
+
+# get a database connection
+ db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp')
+
+# calculate the stat_dict and then create the text for the pu
+# stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id)
+ stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand)
+ db_connection.close_connection()
+
+ pu_text = ""
+ for s in stat_list:
+ number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s)
+ pu_text += number[3] + "\n"
+
+ self.lab.set_text(pu_text)
+ self.window.show_all()
+
+ self.window.set_transient_for(stat_window.window)
+
+# set_keep_above(1) for windows
+ if os.name == 'nt': self.topify_window(self.window)
+
+ def button_press_cb(self, widget, event, *args):
+# This handles all callbacks from button presses on the event boxes in
+# the popup windows. There is a bit of an ugly kludge to separate single-
+# and double-clicks. This is the same code as in the Stat_window class
+ if event.button == 1: # left button event
+ if event.type == gtk.gdk.BUTTON_PRESS: # left button single click
+ if self.sb_click > 0: return
+ self.sb_click = gobject.timeout_add(250, self.single_click, widget)
+ elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click
+ if self.sb_click > 0:
+ gobject.source_remove(self.sb_click)
+ self.sb_click = 0
+ self.double_click(widget, event, *args)
+
+ if event.button == 2: # middle button event
+ pass
+# print "middle button clicked"
+
+ if event.button == 3: # right button event
+ pass
+# print "right button clicked"
+
+ def single_click(self, widget):
+# Callback from the timeout in the single-click finding part of the
+# button press call back. This needs to be modified to get all the
+# arguments from the call.
+ self.sb_click = 0
+ self.window.destroy()
+ return False
+
+ def double_click(self, widget, event, *args):
+ self.toggle_decorated(widget)
+
+ def toggle_decorated(self, widget):
+ top = widget.get_toplevel()
+ (x, y) = top.get_position()
+
+ if top.get_decorated():
+ top.set_decorated(0)
+ top.move(x, y)
+ else:
+ top.set_decorated(1)
+ top.move(x, y)
+
+ def topify_window(self, window):
+ """Set the specified gtk window to stayontop in MS Windows."""
+
+ def windowEnumerationHandler(hwnd, resultList):
+ '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
+ resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
+
+ unique_name = 'unique name for finding this window'
+ real_name = window.get_title()
+ window.set_title(unique_name)
+ tl_windows = []
+ win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
+
+ for w in tl_windows:
+ if w[1] == unique_name:
+# win32gui.ShowWindow(w[0], win32con.SW_HIDE)
+# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE)
+# style |= win32con.WS_EX_TOOLWINDOW
+# style &= ~win32con.WS_EX_APPWINDOW
+# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style)
+# win32gui.ShowWindow(w[0], win32con.SW_SHOW)
+ win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
+
+# notify_id = (w[0],
+# 0,
+# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
+# win32con.WM_USER+20,
+# 0,
+# '')
+# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id)
+#
+ window.set_title(real_name)
+
+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, "Motorway")
+ if t is None:
+ print "Table not found."
+ db = Database.Database(c, 'fpdb', 'holdem')
+
+# for t in tables:
+ win = Hud(t, 10, 'holdem', c, db)
+ win.create(1, c)
+# t.get_details()
+ win.update(8300, db, c)
+
+ gtk.main()
diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py
index 0af06c65..45fe66bd 100644
--- a/pyfpdb/Mucked.py
+++ b/pyfpdb/Mucked.py
@@ -41,30 +41,47 @@ import Configuration
import Database
import Tables
import Hud
-import Mucked
import HandHistory
-class Mucked:
- def __init__(self, parent, db_connection):
-
- self.parent = parent #this is the parent of the mucked cards widget
- self.db_connection = db_connection
+class Aux_Window:
+ def __init__(self, parent, config, db_name):
+ self.config = config
+ self.parent = parent #this is the parent of the mucked cards widget
+ self.db_name = db_name
self.vbox = gtk.VBox()
self.parent.add(self.vbox)
- self.mucked_list = MuckedList (self.vbox, db_connection)
- self.mucked_cards = MuckedCards(self.vbox, db_connection)
+ def update(self):
+ pass
+
+class Stud_mucked(Aux_Window):
+ def __init__(self, parent, config, db_name):
+
+ self.config = config
+ self.parent = parent #this is the parent of the mucked cards widget
+ self.db_name = db_name
+
+ self.vbox = gtk.VBox()
+ self.parent.add(self.vbox)
+
+ self.mucked_list = Stud_list(self.vbox, config, db_name)
+ self.mucked_cards = Stud_cards(self.vbox, config, db_name)
self.mucked_list.mucked_cards = self.mucked_cards
+ self.parent.show_all()
- def update(self, new_hand_id):
- self.mucked_list.update(new_hand_id)
+ def update_data(self, new_hand_id):
+ self.mucked_list.update_data(new_hand_id)
-class MuckedList:
- def __init__(self, parent, db_connection):
+ def update_gui(self, new_hand_id):
+ self.mucked_list.update_gui(new_hand_id)
+
+class Stud_list:
+ def __init__(self, parent, config, db_name):
- self.parent = parent
- self.db_connection = db_connection
+ self.parent = parent
+ self.config = config
+ self.db_name = db_name
# set up a scrolled window to hold the listbox
self.scrolled_window = gtk.ScrolledWindow()
@@ -114,12 +131,26 @@ class MuckedList:
vadj = self.scrolled_window.get_vadjustment()
vadj.set_value(vadj.upper)
self.mucked_cards.update(new_hand_id)
+
+ def update_data(self, new_hand_id):
+ self.info_row = ((new_hand_id, "xxxx", 0), )
+ self.mucked_cards.update_data(new_hand_id)
-class MuckedCards:
- def __init__(self, parent, db_connection):
+ def update_gui(self, new_hand_id):
+ iter = self.liststore.append(self.info_row[0])
+ sel = self.treeview.get_selection()
+ sel.select_iter(iter)
- self.parent = parent #this is the parent of the mucked cards widget
- self.db_connection = db_connection
+ vadj = self.scrolled_window.get_vadjustment()
+ vadj.set_value(vadj.upper)
+ self.mucked_cards.update_gui(new_hand_id)
+
+class Stud_cards:
+ def __init__(self, parent, config, db_name = 'fpdb'):
+
+ self.parent = parent #this is the parent of the mucked cards widget
+ self.config = config
+ self.db_name = db_name
self.card_images = self.get_card_images()
self.seen_cards = {}
@@ -159,44 +190,106 @@ class MuckedCards:
self.parent.add(self.grid)
def translate_cards(self, old_cards):
- pass
+ ranks = ('', '', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
+
+ for c in old_cards.keys():
+ for i in range(1, 8):
+ rank = 'card' + str(i) + 'Value'
+ suit = 'card' + str(i) + 'Suit'
+ key = 'hole_card_' + str(i)
+ if old_cards[c][rank] == 0:
+ old_cards[c][key] = 'xx'
+ else:
+ old_cards[c][key] = ranks[old_cards[c][rank]] + old_cards[c][suit]
+ return old_cards
def update(self, new_hand_id):
- cards = self.db_connection.get_cards(new_hand_id)
+ db_connection = Database.Database(self.config, 'fpdb', '')
+ cards = db_connection.get_cards(new_hand_id)
self.clear()
-
cards = self.translate_cards(cards)
for c in cards.keys():
self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name'])
-
for i in ((0, 'hole_card_1'), (1, 'hole_card_2'), (2, 'hole_card_3'), (3, 'hole_card_4'),
(4, 'hole_card_5'), (5, 'hole_card_6'), (6, 'hole_card_7')):
- if not cards[c][i[1]] == "":
+ if not cards[c][i[1]] == "xx":
self.seen_cards[(i[0], cards[c]['seat_number'] - 1)]. \
set_from_pixbuf(self.card_images[self.split_cards(cards[c][i[1]])])
-
- xml_text = self.db_connection.get_xml(new_hand_id)
- hh = HandHistory.HandHistory(xml_text, ('BETTING'))
-# action in tool tips for 3rd street cards
- tip = "%s" % hh.BETTING.rounds[0]
+ tips = []
+ action = db_connection.get_action_from_hand(new_hand_id)
+ for street in action:
+ temp = ''
+ for act in street:
+ temp = temp + act[0] + " " + act[1] + "s "
+ if act[2] > 0:
+ if act[2]%100 > 0:
+ temp = temp + "%4.2f\n" % (float(act[2])/100)
+ else:
+ temp = temp + "%d\n" % (act[2]/100)
+ else:
+ temp = temp + "\n"
+ tips.append(temp)
+
+## action in tool tips for 3rd street cards
for c in (0, 1, 2):
for r in range(0, self.rows):
- self.eb[(c, r)].set_tooltip_text(tip)
+ self.eb[(c, r)].set_tooltip_text(tips[0])
# action in tools tips for later streets
round_to_col = (0, 3, 4, 5, 6)
- for round in range(1, len(hh.BETTING.rounds)):
- tip = "%s" % hh.BETTING.rounds[round]
+ for round in range(1, len(tips)):
for r in range(0, self.rows):
- self.eb[(round_to_col[round], r)].set_tooltip_text(tip)
+ self.eb[(round_to_col[round], r)].set_tooltip_text(tips[round])
+ db_connection.close_connection()
+
+ def update_data(self, new_hand_id):
+ db_connection = Database.Database(self.config, 'fpdb', '')
+ cards = db_connection.get_cards(new_hand_id)
+ self.clear()
+ self.cards = self.translate_cards(cards)
+
+ self.tips = []
+ action = db_connection.get_action_from_hand(new_hand_id)
+ for street in action:
+ temp = ''
+ for act in street:
+ temp = temp + act[0] + " " + act[1] + "s "
+ if act[2] > 0:
+ if act[2]%100 > 0:
+ temp = temp + "%4.2f\n" % (float(act[2])/100)
+ else:
+ temp = temp + "%d\n" % (act[2]/100)
+ else:
+ temp = temp + "\n"
+ self.tips.append(temp)
+ db_connection.close_connection()
+
+ def update_gui(self, new_hand_id):
+ for c in self.cards.keys():
+ self.grid_contents[(1, self.cards[c]['seat_number'] - 1)].set_text(self.cards[c]['screen_name'])
+ for i in ((0, 'hole_card_1'), (1, 'hole_card_2'), (2, 'hole_card_3'), (3, 'hole_card_4'),
+ (4, 'hole_card_5'), (5, 'hole_card_6'), (6, 'hole_card_7')):
+ if not self.cards[c][i[1]] == "xx":
+ self.seen_cards[(i[0], self.cards[c]['seat_number'] - 1)]. \
+ set_from_pixbuf(self.card_images[self.split_cards(self.cards[c][i[1]])])
+## action in tool tips for 3rd street cards
+ for c in (0, 1, 2):
+ for r in range(0, self.rows):
+ self.eb[(c, r)].set_tooltip_text(self.tips[0])
+
+# action in tools tips for later streets
+ round_to_col = (0, 3, 4, 5, 6)
+ for round in range(1, len(self.tips)):
+ for r in range(0, self.rows):
+ self.eb[(round_to_col[round], r)].set_tooltip_text(self.tips[round])
def split_cards(self, card):
return (card[0], card[1].upper())
def clear(self):
for r in range(0, self.rows):
- self.grid_contents[(1, r)].set_text(" ")
+ self.grid_contents[(1, r)].set_text(" ")
for c in range(0, 7):
self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[('B', 'S')])
self.eb[(c, r)].set_tooltip_text('')
@@ -225,19 +318,19 @@ if __name__== "__main__":
# just read it and pass it to update
new_hand_id = sys.stdin.readline()
new_hand_id = new_hand_id.rstrip() # remove trailing whitespace
- m.update(new_hand_id)
+ m.update_data(new_hand_id)
+ m.update_gui(new_hand_id)
return(True)
config = Configuration.Config()
- db_connection = Database.Database(config, 'fpdb', '')
-
+# db_connection = Database.Database(config, 'fpdb', '')
main_window = gtk.Window()
main_window.set_keep_above(True)
main_window.connect("destroy", destroy)
- m = Mucked(main_window, db_connection)
+ aux_to_call = "Stud_mucked"
+ m = eval("%s(main_window, config, 'fpdb')" % aux_to_call)
main_window.show_all()
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)
-
gtk.main()
diff --git a/pyfpdb/RegressionTest.py b/pyfpdb/RegressionTest.py
index b4dd1b7d..2b09d248 100644
--- a/pyfpdb/RegressionTest.py
+++ b/pyfpdb/RegressionTest.py
@@ -24,63 +24,83 @@
import os
import sys
+import datetime
+import Configuration
import fpdb_db
import fpdb_import
+import fpdb_simple
import FpdbSQLQueries
import unittest
class TestSequenceFunctions(unittest.TestCase):
- def setUp(self):
- """Configure MySQL settings/database and establish connection"""
- self.mysql_settings={ 'db-host':"localhost",
- 'db-backend':2,
- 'db-databaseName':"fpdbtest",
- 'db-user':"fpdb",
- 'db-password':"fpdb"}
- self.mysql_db = fpdb_db.fpdb_db()
- self.mysql_db.connect(self.mysql_settings['db-backend'], self.mysql_settings['db-host'],
- self.mysql_settings['db-databaseName'], self.mysql_settings['db-user'],
- self.mysql_settings['db-password'])
- self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB')
- self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings)
+ def setUp(self):
+ """Configure MySQL settings/database and establish connection"""
+ self.c = Configuration.Config()
+ self.mysql_settings={ 'db-host':"localhost",
+ 'db-backend':2,
+ 'db-databaseName':"fpdbtest",
+ 'db-user':"fpdb",
+ 'db-password':"fpdb"}
+ self.mysql_db = fpdb_db.fpdb_db()
+ self.mysql_db.connect(self.mysql_settings['db-backend'], self.mysql_settings['db-host'],
+ self.mysql_settings['db-databaseName'], self.mysql_settings['db-user'],
+ self.mysql_settings['db-password'])
+ self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB')
+ self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings, self.c)
+ self.mysqlimporter.setCallHud(False)
-# """Configure Postgres settings/database and establish connection"""
-# self.pg_settings={ 'db-host':"localhost", 'db-backend':3, 'db-databaseName':"fpdbtest", 'db-user':"fpdb", 'db-password':"fpdb"}
-# self.pg_db = fpdb_db.fpdb_db()
-# self.pg_db.connect(self.pg_settings['db-backend'], self.pg_settings['db-host'],
-# self.pg_settings['db-databaseName'], self.pg_settings['db-user'],
-# self.pg_settings['db-password'])
-# self.pgdict = FpdbSQLQueries.FpdbSQLQueries('PostgreSQL')
+# """Configure Postgres settings/database and establish connection"""
+# self.pg_settings={ 'db-host':"localhost", 'db-backend':3, 'db-databaseName':"fpdbtest", 'db-user':"fpdb", 'db-password':"fpdb"}
+# self.pg_db = fpdb_db.fpdb_db()
+# self.pg_db.connect(self.pg_settings['db-backend'], self.pg_settings['db-host'],
+# self.pg_settings['db-databaseName'], self.pg_settings['db-user'],
+# self.pg_settings['db-password'])
+# self.pgdict = FpdbSQLQueries.FpdbSQLQueries('PostgreSQL')
- def testDatabaseConnection(self):
- """Test all supported DBs"""
- self.result = self.mysql_db.cursor.execute(self.mysqldict.query['list_tables'])
- self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
+ def testDatabaseConnection(self):
+ """Test all supported DBs"""
+ self.result = self.mysql_db.cursor.execute(self.mysqldict.query['list_tables'])
+ self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
-# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
-# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
+# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
+# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
- def testMySQLRecreateTables(self):
- """Test droping then recreating fpdb table schema"""
- self.mysql_db.recreate_tables()
- self.result = self.mysql_db.cursor.execute("SHOW TABLES")
- self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
+ def testMySQLRecreateTables(self):
+ """Test droping then recreating fpdb table schema"""
+ self.mysql_db.recreate_tables()
+ self.result = self.mysql_db.cursor.execute("SHOW TABLES")
+ self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
- def testImportHandHistoryFiles(self):
- """Test import of single HH file"""
- self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")
- self.mysqlimporter.runImport()
- self.mysqlimporter.addImportDirectory("regression-test-files/hand-histories")
- self.mysqlimporter.runImport()
+ def testPokerStarsHHDate(self):
+ latest = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]"
+ previous = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/08/17 - 01:14:43 (ET)"
+ older1 = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/09/07 06:23:14 ET"
-# def testPostgresSQLRecreateTables(self):
-# """Test droping then recreating fpdb table schema"""
-# self.pg_db.recreate_tables()
-# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
-# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
+ result = fpdb_simple.parseHandStartTime(older1, "ps")
+ self.failUnless(result==datetime.datetime(2008,9,7,11,23,14),
+ "Date incorrect, expected: 2008-09-07 11:23:14 got: " + str(result))
+ result = fpdb_simple.parseHandStartTime(latest, "ps")
+ self.failUnless(result==datetime.datetime(2008,11,12,15,00,48),
+ "Date incorrect, expected: 2008-11-12 15:00:48 got: " + str(result))
+ result = fpdb_simple.parseHandStartTime(previous, "ps")
+ self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
+ "Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result))
+
+ def testImportHandHistoryFiles(self):
+ """Test import of single HH file"""
+ self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")
+ self.mysqlimporter.runImport()
+ self.mysqlimporter.addImportDirectory("regression-test-files/hand-histories")
+ self.mysqlimporter.runImport()
+
+# def testPostgresSQLRecreateTables(self):
+# """Test droping then recreating fpdb table schema"""
+# self.pg_db.recreate_tables()
+# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
+# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
if __name__ == '__main__':
unittest.main()
diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py
index 7ff0b18c..75b6437c 100644
--- a/pyfpdb/SQL.py
+++ b/pyfpdb/SQL.py
@@ -237,18 +237,80 @@ class Sql:
GROUP BY HudCache.PlayerId
"""
-# FROM HudCache, Hands
-# WHERE HudCache.PlayerId in
-# (SELECT PlayerId FROM HandsPlayers
-# WHERE handId = %s)
-# AND Hands.id = %s
-# AND Hands.gametypeId = HudCache.gametypeId
-
-# AND PlayerId LIKE %s
-# HudCache.gametypeId AS gametypeId,
-# activeSeats AS n_active,
-# position AS position,
-# HudCache.tourneyTypeId AS tourneyTypeId,
+# same as above except stats are aggregated for all blind/limit levels
+ self.query['get_stats_from_hand_aggregated'] = """
+ SELECT HudCache.playerId AS player_id,
+ sum(HDs) AS n,
+ sum(street0VPI) AS vpip,
+ sum(street0Aggr) AS pfr,
+ sum(street0_3B4BChance) AS TB_opp_0,
+ sum(street0_3B4BDone) AS TB_0,
+ sum(street1Seen) AS saw_f,
+ sum(street1Seen) AS saw_1,
+ sum(street2Seen) AS saw_2,
+ sum(street3Seen) AS saw_3,
+ sum(street4Seen) AS saw_4,
+ sum(sawShowdown) AS sd,
+ sum(street1Aggr) AS aggr_1,
+ sum(street2Aggr) AS aggr_2,
+ sum(street3Aggr) AS aggr_3,
+ sum(street4Aggr) AS aggr_4,
+ sum(otherRaisedStreet1) AS was_raised_1,
+ sum(otherRaisedStreet2) AS was_raised_2,
+ sum(otherRaisedStreet3) AS was_raised_3,
+ sum(otherRaisedStreet4) AS was_raised_4,
+ sum(foldToOtherRaisedStreet1) AS f_freq_1,
+ sum(foldToOtherRaisedStreet2) AS f_freq_2,
+ sum(foldToOtherRaisedStreet3) AS f_freq_3,
+ sum(foldToOtherRaisedStreet4) AS f_freq_4,
+ sum(wonWhenSeenStreet1) AS w_w_s_1,
+ sum(wonAtSD) AS wmsd,
+ sum(stealAttemptChance) AS steal_opp,
+ sum(stealAttempted) AS steal,
+ sum(foldSbToStealChance) AS SBstolen,
+ sum(foldedSbToSteal) AS SBnotDef,
+ sum(foldBbToStealChance) AS BBstolen,
+ sum(foldedBbToSteal) AS BBnotDef,
+ sum(street1CBChance) AS CB_opp_1,
+ sum(street1CBDone) AS CB_1,
+ sum(street2CBChance) AS CB_opp_2,
+ sum(street2CBDone) AS CB_2,
+ sum(street3CBChance) AS CB_opp_3,
+ sum(street3CBDone) AS CB_3,
+ sum(street4CBChance) AS CB_opp_4,
+ sum(street4CBDone) AS CB_4,
+ sum(foldToStreet1CBChance) AS f_cb_opp_1,
+ sum(foldToStreet1CBDone) AS f_cb_1,
+ sum(foldToStreet2CBChance) AS f_cb_opp_2,
+ sum(foldToStreet2CBDone) AS f_cb_2,
+ sum(foldToStreet3CBChance) AS f_cb_opp_3,
+ sum(foldToStreet3CBDone) AS f_cb_3,
+ sum(foldToStreet4CBChance) AS f_cb_opp_4,
+ sum(foldToStreet4CBDone) AS f_cb_4,
+ sum(totalProfit) AS net,
+ sum(street1CheckCallRaiseChance) AS ccr_opp_1,
+ sum(street1CheckCallRaiseDone) AS ccr_1,
+ sum(street2CheckCallRaiseChance) AS ccr_opp_2,
+ sum(street2CheckCallRaiseDone) AS ccr_2,
+ sum(street3CheckCallRaiseChance) AS ccr_opp_3,
+ sum(street3CheckCallRaiseDone) AS ccr_3,
+ sum(street4CheckCallRaiseChance) AS ccr_opp_4,
+ sum(street4CheckCallRaiseDone) AS ccr_4
+ FROM HudCache, Hands
+ WHERE HudCache.PlayerId in
+ (SELECT PlayerId FROM HandsPlayers
+ WHERE handId = %s)
+ AND Hands.id = %s
+ AND HudCache.gametypeId in
+ (SELECT gt1.id from Gametypes gt1, Gametypes gt2, Hands
+ WHERE gt1.siteid = gt2.siteid
+ AND gt1.type = gt2.type
+ AND gt1.category = gt2.category
+ AND gt1.limittype = gt2.limittype
+ AND gt2.id = Hands.gametypeId
+ AND Hands.id = %s)
+ GROUP BY HudCache.PlayerId
+ """
self.query['get_players_from_hand'] = """
SELECT HandsPlayers.playerId, seatNo, name
@@ -288,15 +350,15 @@ class Sql:
order by seatNo
"""
-# self.query['get_hand_info'] = """
-# SELECT
-# game_id,
-# CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand,
-# total_won-total_bet AS net
-# FROM game_players
-# WHERE game_id = %s AND player_id = 3
-# """
-
+ self.query['get_action_from_hand'] = """
+ SELECT street, Players.name, HandsActions.action, HandsActions.amount, actionno
+ FROM Players, HandsActions, HandsPlayers
+ WHERE HandsActions.action != 'blind'
+ AND HandsPlayers.handid = %s
+ AND HandsPlayers.playerid = Players.id
+ AND HandsActions.handPlayerId = HandsPlayers.id
+ ORDER BY street, actionno
+ """
if __name__== "__main__":
# just print the default queries and exit
s = Sql(game = 'razz', type = 'ptracks')
diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py
index 50a502e3..80b3c7f1 100644
--- a/pyfpdb/Stats.py
+++ b/pyfpdb/Stats.py
@@ -89,14 +89,14 @@ def vpip(stat_dict, player):
'v=%3.1f' % (100*stat) + '%',
'vpip=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']),
- 'vpip'
+ 'Voluntarily Put In Pot %'
)
except: return (stat,
'%3.1f' % (0) + '%',
- 'w=%3.1f' % (0) + '%',
- 'wtsd=%3.1f' % (0) + '%',
+ 'v=%3.1f' % (0) + '%',
+ 'vpip=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
- 'wtsd'
+ 'Voluntarily Put In Pot %'
)
def vpip_0(stat_dict, player):
@@ -129,7 +129,7 @@ def pfr(stat_dict, player):
'p=%3.1f' % (100*stat) + '%',
'pfr=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']),
- 'pfr'
+ 'Pre-Flop Raise %'
)
except:
return (stat,
@@ -137,7 +137,7 @@ def pfr(stat_dict, player):
'p=%3.1f' % (0) + '%',
'pfr=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
- 'pfr'
+ 'Pre-Flop Raise %'
)
def pfr_0(stat_dict, player):
@@ -214,7 +214,7 @@ def saw_f(stat_dict, player):
'sf=%3.1f' % (100*stat) + '%',
'saw_f=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['saw_f'], stat_dict[player]['n']),
- 'saw_f'
+ 'Flop Seen %'
)
except:
stat = 0.0
@@ -225,7 +225,7 @@ def saw_f(stat_dict, player):
'sf=%3.1f' % (stat) + '%',
'saw_f=%3.1f' % (stat) + '%',
'(%d/%d)' % (num, den),
- 'saw_f'
+ 'Flop Seen %'
)
def n(stat_dict, player):
@@ -303,12 +303,11 @@ def f_SB_steal(stat_dict, player):
)
except:
return (stat,
- '%3.1f' % (0) + '%',
- 'fSB=%3.1f' % (0) + '%',
- 'fSB_s=%3.1f' % (0) + '%',
- '(%d/%d)' % (0, 0),
- '% folded SB to steal'
- )
+ 'NA',
+ 'fSB=NA',
+ 'fSB_s=NA',
+ '0/0',
+ '% folded SB to steal')
def f_BB_steal(stat_dict, player):
""" Folded BB to steal."""
@@ -324,12 +323,11 @@ def f_BB_steal(stat_dict, player):
)
except:
return (stat,
- '%3.1f' % (0) + '%',
- 'fBB=%3.1f' % (0) + '%',
- 'fBB_s=%3.1f' % (0) + '%',
- '(%d/%d)' % (0, 0),
- '% folded BB to steal'
- )
+ 'NA',
+ 'fBB=NA',
+ 'fBB_s=NA',
+ '0/0',
+ '% folded BB to steal')
def three_B_0(stat_dict, player):
""" Three bet preflop/3rd."""
@@ -454,7 +452,7 @@ def a_freq_4(stat_dict, player):
'a4=%3.1f' % (0) + '%',
'a_fq_4=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
- 'Aggression Freq flop/4th'
+ 'Aggression Freq 7th'
)
def a_freq_123(stat_dict, player):
diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py
old mode 100644
new mode 100755
index 752c5929..9f5360e2
--- a/pyfpdb/Tables.py
+++ b/pyfpdb/Tables.py
@@ -7,7 +7,6 @@ of Table_Window objects representing the windows found.
"""
# Copyright 2008, 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
@@ -40,7 +39,35 @@ if os.name == 'nt':
# 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 info.has_key('number'): self.number = info['number']
+ if info.has_key('exe'): self.exe = info['exe']
+ if info.has_key('width'): self.width = info['width']
+ if info.has_key('height'): self.height = info['height']
+ if info.has_key('x'): self.x = info['x']
+ if info.has_key('y'): self.y = info['y']
+ if info.has_key('site'): self.site = info['site']
+ if info.has_key('title'): self.title = info['title']
+ if info.has_key('name'): self.name = info['name']
+
def __str__(self):
# __str__ method for testing
temp = 'TableWindow object\n'
@@ -51,13 +78,10 @@ class Table_Window:
temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table)
return temp
- def get_details(table):
- table.game = 'razz'
- table.max = 8
- table.struture = 'limit'
- table.tournament = 0
-
+############################################################################
+# 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':
@@ -66,86 +90,99 @@ def discover(c):
tables = discover_mac(c)
else:
tables = {}
-
- return(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':
- table = discover_posix_by_name(c, tablename)
+ info = discover_posix_by_name(c, tablename)
elif os.name == 'nt':
- table = discover_nt_by_name(c, tablename)
+ info = discover_nt_by_name(c, tablename)
elif os.name == 'mac':
- table = discover_mac_by_name(c, tablename)
+ info = discover_mac_by_name(c, tablename)
else:
- table = None
- return(table)
+ return None
+ if info == 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."""
+ """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
- if re.search('Lobby', listing): continue
- if re.search('Instant Hand History', listing): continue
- if not re.search('Logged In as ', listing, re.IGNORECASE): continue
- if re.search('\"Full Tilt Poker\"', listing): continue # FTP Lobby
- for s in c.supported_sites.keys():
- if re.search(c.supported_sites[s].table_finder, listing):
- mo = re.match('\s+([\dxabcdef]+) (.+):.+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing)
- if mo.group(2) == '(has no name)': continue
- if re.match('[\(\)\d\s]+', mo.group(2)): continue # this is a popup
- tw = Table_Window()
- tw.site = c.supported_sites[s].site_name
- tw.number = int(mo.group(1), 0)
- tw.title = mo.group(2)
- tw.width = int( mo.group(3) )
- tw.height = int( mo.group(4) )
- tw.x = int (mo.group(5) )
- tw.y = int (mo.group(6) )
- tw.title = re.sub('\"', '', tw.title)
-
-# use this eval thingie to call the title bar decoder specified in the config file
- eval("%s(tw)" % c.supported_sites[s].decoder)
-
+ for s in c.get_supported_sites():
+ params = c.get_site_parameters(s)
+ if re.search(params['table_finder'], listing):
+ if re.search('Lobby', listing): continue
+ if re.search('Instant Hand History', listing): continue
+ if re.search('\"Full Tilt Poker\"', listing): continue # FTP Lobby
+ if re.search('History for table:', listing): continue
+ if re.search('has no name', listing): continue
+ info = decode_xwininfo(c, listing)
+ if info['site'] == 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):
- tables = discover_posix(c)
- for t in tables:
- if tables[t].name.find(tablename) > -1:
- return tables[t]
- return None
+ """Find an XWindows poker client of the given name."""
+ for listing in os.popen('xwininfo -root -tree').readlines():
+ if re.search(tablename, listing):
+ if re.search('History for table:', listing): continue
+ info = decode_xwininfo(c, listing)
+ if not info['name'] == tablename: continue
+ return info
+ return False
-#
-# The discover_xx functions query the system and report on the poker clients
-# currently displayed on the screen. The discover_posix should give you
-# some idea how to support other systems.
-#
-# discover_xx() returns a dict of TableWindow objects--one TableWindow
-# object for each poker client table on the screen.
-#
-# Each TableWindow object must have the following attributes correctly populated:
-# 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.
-# 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.
+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%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 False
-def win_enum_handler(hwnd, titles):
- titles[hwnd] = win32gui.GetWindowText(hwnd)
-
-
-def child_enum_handler(hwnd, children):
- print hwnd, win32.GetWindowRect(hwnd)
+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."""
#
@@ -185,72 +222,88 @@ def discover_nt(c):
return tables
def discover_nt_by_name(c, tablename):
- # this is pretty much identical to the 'search all windows for all poker sites' code, but made to dig just for a specific table name
- # it could be implemented a bunch better - and we need to not assume the width/height thing that (steffen?) assumed above, we should
- # be able to dig up the window's titlebar handle and get it's information, and such .. but.. for now, i guess this will work.
- # - eric
+ """Finds poker client window with the given table name."""
+ titles = {}
+ win32gui.EnumWindows(win_enum_handler, titles)
+ for hwnd in titles.keys():
+ if titles[hwnd].find(tablename) == -1: continue
+ if titles[hwnd].find("History for table:") > -1: continue
+ if titles[hwnd].find("HUD:") > -1: continue
+ if titles[hwnd].find("Chat:") > -1: continue
+ return decode_windows(c, titles[hwnd], hwnd)
+ return False
+
+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.keys():
+ if re.search(search_string, titles[hwnd]):
+ return decode_windows(c, titles[hwnd], hwnd)
+ return False
+
+def get_nt_exe(hwnd):
+ """Finds the name of the executable that the given window handle belongs to."""
+ processid = win32process.GetWindowThreadProcessId(hwnd)
+ pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
+ return win32process.GetModuleFileNameEx(pshandle, 0)
+
+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
- titles = {}
-# tables = discover_nt(c)
- win32gui.EnumWindows(win_enum_handler, titles)
- for s in c.supported_sites.keys():
- for hwnd in titles.keys():
- processid = win32process.GetWindowThreadProcessId(hwnd)
- pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
- exe = win32process.GetModuleFileNameEx(pshandle, 0)
- if exe.find(c.supported_sites[s].table_finder) == -1:
- continue
- if titles[hwnd].find(tablename) > -1:
- if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
- 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
- tw.site = c.supported_sites[s].site_name
- if not tw.site == "Unknown" and not c.supported_sites[tw.site].decoder == "Unknown":
- eval("%s(tw)" % c.supported_sites[tw.site].decoder)
- else:
- tw.name = tablename
- return tw
-
- # if we don't find anything by process name, let's search one more time, and call it Unknown ?
- for hwnd in titles.keys():
- if titles[hwnd].find(tablename) > -1:
- if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
- 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
- tw.site = "Unknown"
- tw.name = tablename
- return tw
-
+
+ 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)
+
+ 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):
+ 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 discover_mac(c):
- """ Poker client table window finder for Macintosh."""
- tables = {}
- return tables
-
-def discover_mac_by_name(c, tablename):
- # again, i have no mac to test this on, sorry -eric
- return discover_mac(c)
-
+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\)', ' \(2\)',
+ ' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
+ ' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
+ name = re.sub(pattern, '', name)
+ name = name.rstrip()
+ return name
def pokerstars_decode_table(tw):
-# extract the table name OR the tournament number and table name from the title
-# other info in title is redundant with data in the database
+# 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)
@@ -260,12 +313,8 @@ def pokerstars_decode_table(tw):
tw.name = name
else:
tw.tournament = None
- for pattern in [' no all-in', ' fast', ',', ' 50BB min']:
- name = re.sub(pattern, '', name)
- name = re.sub('\s+$', '', name)
- tw.name = name
-
- mo = re.search('(Razz|Stud H/L|Stud|Omaha H/L|Omaha|Hold\'em|5-Card Draw|Triple Draw 2-7 Lowball)', tw.title)
+ 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)
@@ -288,25 +337,103 @@ def pokerstars_decode_table(tw):
pass
def fulltilt_decode_table(tw):
-# extract the table name OR the tournament number and table name from the title
-# other info in title is redundant with data in the database
+# 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
- for pattern in [' (6 max)', ' (heads up)', ' (deep)',
- ' (deep hu)', ' (deep 6)', ' (2)',
- ' (edu)', ' (edu, 6 max)', ' (6)' ]:
+ 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\)', ' \(2\)',
+ ' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
+ ' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
name = re.sub(pattern, '', name)
-# (tw.name, trash) = name.split(r' (', 1)
- tw.name = name.rstrip()
+ 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
+
+#def discover_nt_by_name(c, tablename):
+# # this is pretty much identical to the 'search all windows for all poker sites' code, but made to dig just for a specific table name
+# # it could be implemented a bunch better - and we need to not assume the width/height thing that (steffen?) assumed above, we should
+# # be able to dig up the window's titlebar handle and get it's information, and such .. but.. for now, i guess this will work.
+# # - eric
+# b_width = 3
+# tb_height = 29
+# titles = {}
+## tables = discover_nt(c)
+# win32gui.EnumWindows(win_enum_handler, titles)
+# for s in c.supported_sites.keys():
+# for hwnd in titles.keys():
+# processid = win32process.GetWindowThreadProcessId(hwnd)
+# pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
+# exe = win32process.GetModuleFileNameEx(pshandle, 0)
+# if exe.find(c.supported_sites[s].table_finder) == -1:
+# continue
+# if titles[hwnd].find(tablename) > -1:
+# if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
+# 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
+# tw.site = c.supported_sites[s].site_name
+# if not tw.site == "Unknown" and not c.supported_sites[tw.site].decoder == "Unknown":
+# eval("%s(tw)" % c.supported_sites[tw.site].decoder)
+# else:
+# tw.name = tablename
+# return tw
+#
+# # if we don't find anything by process name, let's search one more time, and call it Unknown ?
+# for hwnd in titles.keys():
+# if titles[hwnd].find(tablename) > -1:
+# if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
+# 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
+# tw.site = "Unknown"
+# tw.name = tablename
+# return tw
+#
+# return None
if __name__=="__main__":
c = Configuration.Config()
- print discover_table_by_name(c, "Catacaos")
+
+ print discover_table_by_name(c, "Howard Lederer")
+ print discover_tournament_table(c, "118942908", "3")
+
tables = discover(c)
-
for t in tables.keys():
- print "t = ", t
print tables[t]
print "press enter to continue"
diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py
index 8b4af06f..7c442ef0 100644
--- a/pyfpdb/fpdb.py
+++ b/pyfpdb/fpdb.py
@@ -1,449 +1,461 @@
-#!/usr/bin/python
-
-#Copyright 2008 Steffen Jobbagy-Felso
-#This program is free software: you can redistribute it and/or modify
-#it under the terms of the GNU Affero General Public License as published by
-#the Free Software Foundation, version 3 of the License.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU Affero General Public License
-#along with this program. If not, see .
-#In the "official" distribution you can find the license in
-#agpl-3.0.txt in the docs folder of the package.
-
-import os
-import sys
-from optparse import OptionParser
-
-
-parser = OptionParser()
-parser.add_option("-x", "--errorsToConsole", action="store_true",
- help="If passed error output will go to the console rather than .")
-(options, sys.argv) = parser.parse_args()
-
-if not options.errorsToConsole:
- print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
- errorFile = open('fpdb-error-log.txt', 'w', 0)
- sys.stderr = errorFile
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-
-import fpdb_db
-import fpdb_simple
-import GuiBulkImport
-import GuiTableViewer
-import GuiAutoImport
-import GuiGraphViewer
-import FpdbSQLQueries
-import Configuration
-
-class fpdb:
- def tab_clicked(self, widget, tab_name):
- """called when a tab button is clicked to activate that tab"""
- #print "start of tab_clicked"
- self.display_tab(tab_name)
- #end def tab_clicked
-
- def add_and_display_tab(self, new_tab, new_tab_name):
- """just calls the component methods"""
- self.add_tab(new_tab, new_tab_name)
- self.display_tab(new_tab_name)
- #end def add_and_display_tab
-
- def add_tab(self, new_tab, new_tab_name):
- """adds a tab, namely creates the button and displays it and appends all the relevant arrays"""
- #print "start of add_tab"
- for i in self.tab_names: #todo: check this is valid
- if i==new_tab_name:
- raise fpdb_simple.FpdbError("duplicate tab_name not permitted")
-
- self.tabs.append(new_tab)
- self.tab_names.append(new_tab_name)
-
- new_tab_sel_button=gtk.ToggleButton(new_tab_name)
- new_tab_sel_button.connect("clicked", self.tab_clicked, new_tab_name)
- self.tab_box.add(new_tab_sel_button)
- new_tab_sel_button.show()
- self.tab_buttons.append(new_tab_sel_button)
- #end def add_tab
-
- def display_tab(self, new_tab_name):
- """displays the indicated tab"""
- #print "start of display_tab, len(self.tab_names):",len(self.tab_names)
- tab_no=-1
- #if len(self.tab_names)>1:
- for i in range(len(self.tab_names)):
- #print "display_tab, new_tab_name:",new_tab_name," self.tab_names[i]:", self.tab_names[i]
- if (new_tab_name==self.tab_names[i]):
- tab_no=i
- #self.tab_buttons[i].set_active(False)
- #else:
- # tab_no=0
-
- #current_tab_no=-1
- for i in range(len(self.tab_names)):
- if self.current_tab==self.tabs[i]:
- #self.tab_buttons[i].set_active(False)
- pass
-
- if tab_no==-1:
- raise fpdb_simple.FpdbError("invalid tab_no")
- else:
- self.main_vbox.remove(self.current_tab)
- #self.current_tab.destroy()
- self.current_tab=self.tabs[tab_no]
- self.main_vbox.add(self.current_tab)
- self.tab_buttons[tab_no].set_active(True)
- self.current_tab.show()
- #end def display_tab
-
- def delete_event(self, widget, event, data=None):
- return False
- #end def delete_event
-
- def destroy(self, widget, data=None):
- self.quit(widget, data)
- #end def destroy
-
- def dia_about(self, widget, data):
- print "todo: implement dia_about"
- #end def dia_about
-
- def dia_create_del_database(self, widget, data):
- print "todo: implement dia_create_del_database"
- obtain_global_lock()
- #end def dia_create_del_database
-
- def dia_create_del_user(self, widget, data):
- print "todo: implement dia_create_del_user"
- obtain_global_lock()
- #end def dia_create_del_user
-
- def dia_database_stats(self, widget, data):
- print "todo: implement dia_database_stats"
- #string=fpdb_db.getDbStats(db, cursor)
- #end def dia_database_stats
-
- def dia_delete_db_parts(self, widget, data):
- print "todo: implement dia_delete_db_parts"
- obtain_global_lock()
- #end def dia_delete_db_parts
-
- def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
- print "todo: implement dia_edit_profile"
- obtain_global_lock()
- #end def dia_edit_profile
-
- def dia_export_db(self, widget, data):
- print "todo: implement dia_export_db"
- obtain_global_lock()
- #end def dia_export_db
-
- def dia_get_db_root_credentials(self):
- """obtains db root credentials from user"""
- print "todo: implement dia_get_db_root_credentials"
-# user, pw=None, None
-#
-# dialog=gtk.Dialog(title="DB Credentials needed", parent=None, flags=0,
-# buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,"Connect and recreate",gtk.RESPONSE_OK))
-#
-# label_warning1=gtk.Label("Please enter credentials for a database user for "+self.host+" that has permissions to create a database.")
-#
-#
-# label_user=gtk.Label("Username")
-# dialog.vbox.add(label_user)
-# label_user.show()
-#
-# response=dialog.run()
-# dialog.destroy()
-# return (user, pw, response)
- #end def dia_get_db_root_credentials
-
- def dia_import_db(self, widget, data):
- print "todo: implement dia_import_db"
- obtain_global_lock()
- #end def dia_import_db
-
- def dia_licensing(self, widget, data):
- print "todo: implement dia_licensing"
- #end def dia_licensing
-
- def dia_load_profile(self, widget, data):
- """Dialogue to select a file to load a profile from"""
- self.obtain_global_lock()
- chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
- action=gtk.FILE_CHOOSER_ACTION_OPEN,
- buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
- chooser.set_filename(self.profile)
-
- response = chooser.run()
- chooser.destroy()
- if response == gtk.RESPONSE_OK:
- self.load_profile(chooser.get_filename())
- elif response == gtk.RESPONSE_CANCEL:
- print 'User cancelled loading profile'
- #end def dia_load_profile
-
- def dia_recreate_tables(self, widget, data):
- """Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
- self.obtain_global_lock()
- dia_confirm=gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
- buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
- diastring=("Please confirm that you want to (re-)create the tables. If there already are tables in the database "+self.db.database+" on "+self.db.host+" they will be deleted.")
- dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted
-
- response=dia_confirm.run()
- dia_confirm.destroy()
- if response == gtk.RESPONSE_YES:
- self.db.recreate_tables()
- elif response == gtk.RESPONSE_NO:
- print 'User cancelled recreating tables'
- #end def dia_recreate_tables
-
- def dia_regression_test(self, widget, data):
- print "todo: implement dia_regression_test"
- self.obtain_global_lock()
- #end def dia_regression_test
-
- def dia_save_profile(self, widget, data):
- print "todo: implement dia_save_profile"
- #end def dia_save_profile
-
- def diaSetupWizard(self, path):
- print "todo: implement setup wizard"
- print "setup wizard not implemented - please create the default configuration file:", path
- diaSetupWizard = gtk.Dialog(title="Fatal Error - Config File Missing", parent=None, flags=0, buttons=(gtk.STOCK_QUIT,gtk.RESPONSE_OK))
-
- label = gtk.Label("Please copy the config file from the docs folder to:")
- diaSetupWizard.vbox.add(label)
- label.show()
-
- label = gtk.Label(path)
- diaSetupWizard.vbox.add(label)
- label.show()
-
- label = gtk.Label("and edit it according to the install documentation at http://fpdb.sourceforge.net")
- diaSetupWizard.vbox.add(label)
- label.show()
-
- response = diaSetupWizard.run()
- sys.exit(1)
- #end def diaSetupWizard
-
- def get_menu(self, window):
- """returns the menu for this program"""
- accel_group = gtk.AccelGroup()
- self.item_factory = gtk.ItemFactory(gtk.MenuBar, "", accel_group)
- self.item_factory.create_items(self.menu_items)
- window.add_accel_group(accel_group)
- return self.item_factory.get_widget("")
- #end def get_menu
-
- def load_profile(self):
- """Loads profile from the provided path name."""
- self.settings = {}
- if (os.sep=="/"):
- self.settings['os']="linuxmac"
- else:
- self.settings['os']="windows"
-
- 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_default_paths())
-
- if self.db!=None:
- self.db.disconnect()
-
- self.db = fpdb_db.fpdb_db()
- #print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
- self.db.connect(self.settings['db-backend'], self.settings['db-host'], self.settings['db-databaseName'], self.settings['db-user'], self.settings['db-password'])
- if self.db.wrongDbVersion:
- diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
-
- label = gtk.Label("An invalid DB version or missing tables have been detected.")
- diaDbVersionWarning.vbox.add(label)
- label.show()
-
- label = gtk.Label("This error is not necessarily fatal but it is strongly recommended that you recreate the tables by using the Database menu.")
- diaDbVersionWarning.vbox.add(label)
- label.show()
-
- label = gtk.Label("Not doing this will likely lead to misbehaviour including fpdb crashes, corrupt data etc.")
- diaDbVersionWarning.vbox.add(label)
- label.show()
-
- response = diaDbVersionWarning.run()
- diaDbVersionWarning.destroy()
-
- # Database connected to successfully, load queries to pass on to other classes
- self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
- #end def load_profile
-
- def not_implemented(self):
- print "todo: called unimplemented menu entry (users: pls ignore this)"#remove this once more entries are implemented
- #end def not_implemented
-
- def obtain_global_lock(self):
- print "todo: implement obtain_global_lock (users: pls ignore this)"
- #end def obtain_global_lock
-
- def quit(self, widget, data):
- print "Quitting normally"
- #check if current settings differ from profile, if so offer to save or abort
- self.db.disconnect()
- gtk.main_quit()
- #end def quit_cliecked
-
- def release_global_lock(self):
- print "todo: implement release_global_lock"
- #end def release_global_lock
-
- def tab_abbreviations(self, widget, data):
- print "todo: implement tab_abbreviations"
- #end def tab_abbreviations
-
- def tab_auto_import(self, widget, data):
- """opens the auto import tab"""
- new_aimp_thread=GuiAutoImport.GuiAutoImport(self.settings, self.config)
- self.threads.append(new_aimp_thread)
- aimp_tab=new_aimp_thread.get_vbox()
- self.add_and_display_tab(aimp_tab, "Auto Import")
- #end def tab_auto_import
-
- def tab_bulk_import(self, widget, data):
- """opens a tab for bulk importing"""
- #print "start of tab_bulk_import"
- new_import_thread=GuiBulkImport.GuiBulkImport(self.db, self.settings, self.config)
- self.threads.append(new_import_thread)
- bulk_tab=new_import_thread.get_vbox()
- self.add_and_display_tab(bulk_tab, "Bulk Import")
- #end def tab_bulk_import
-
- def tab_main_help(self, widget, data):
- """Displays a tab with the main fpdb help screen"""
- #print "start of tab_main_help"
- mh_tab=gtk.Label("""Welcome to Fpdb!
-For documentation please visit our website at http://fpdb.sourceforge.net/ or check the docs directory in the fpdb folder.
-Please note that default.conf is no longer needed nor used, all configuration now happens in HUD_config.xml
-This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
- self.add_and_display_tab(mh_tab, "Help")
- #end def tab_main_help
-
- def tab_table_viewer(self, widget, data):
- """opens a table viewer tab"""
- #print "start of tab_table_viewer"
- new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings)
- self.threads.append(new_tv_thread)
- tv_tab=new_tv_thread.get_vbox()
- self.add_and_display_tab(tv_tab, "Table Viewer")
- #end def tab_table_viewer
-
- def tabGraphViewer(self, widget, data):
- """opens a graph viewer tab"""
- #print "start of tabGraphViewer"
- new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config)
- self.threads.append(new_gv_thread)
- gv_tab=new_gv_thread.get_vbox()
- self.add_and_display_tab(gv_tab, "Graphs")
- #end def tabGraphViewer
-
- def __init__(self):
- self.threads=[]
- self.db=None
- self.config = Configuration.Config()
- self.load_profile()
-
- self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
- self.window.connect("delete_event", self.delete_event)
- self.window.connect("destroy", self.destroy)
- self.window.set_title("Free Poker DB - version: alpha9+, p143 or higher")
- self.window.set_border_width(1)
- self.window.set_size_request(1020,400)
- self.window.set_resizable(True)
-
- self.menu_items = (
- ( "/_Main", None, None, 0, "" ),
- ( "/Main/_Load Profile (broken)", "L", self.dia_load_profile, 0, None ),
- ( "/Main/_Edit Profile (todo)", "E", self.dia_edit_profile, 0, None ),
- ( "/Main/_Save Profile (todo)", None, self.dia_save_profile, 0, None ),
- ("/Main/sep1", None, None, 0, "" ),
- ("/Main/_Quit", "Q", self.quit, 0, None ),
- ("/_Import", None, None, 0, "" ),
- ("/Import/_Bulk Import", "B", self.tab_bulk_import, 0, None ),
- ("/Import/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ),
- ("/Import/Auto _Rating (todo)", "R", self.not_implemented, 0, None ),
- ("/_Viewers", None, None, 0, "" ),
- ("/_Viewers/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ),
- ("/Viewers/_Graphs", "G", self.tabGraphViewer, 0, None ),
- ("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ),
- ("/Viewers/Player _Details (todo)", None, self.not_implemented, 0, None ),
- ("/Viewers/_Player Stats (tabulated view) (todo)", None, self.not_implemented, 0, None ),
- ("/Viewers/Starting _Hands (todo)", None, self.not_implemented, 0, None ),
- ("/Viewers/_Session Replayer (todo)", None, self.not_implemented, 0, None ),
- ("/Viewers/Poker_table Viewer (mostly obselete)", "T", self.tab_table_viewer, 0, None ),
- #( "/Viewers/Tourney Replayer
- ( "/_Database", None, None, 0, "" ),
- ( "/Database/Create or Delete _Database (todo)", None, self.dia_create_del_database, 0, None ),
- ( "/Database/Create or Delete _User (todo)", None, self.dia_create_del_user, 0, None ),
- ( "/Database/Create or Recreate _Tables", None, self.dia_recreate_tables, 0, None ),
- ( "/Database/_Statistics (todo)", None, self.dia_database_stats, 0, None ),
- ( "/D_ebugging", None, None, 0, "" ),
- ( "/Debugging/_Delete Parts of Database (todo)", None, self.dia_delete_db_parts, 0, None ),
- ( "/Debugging/_Export DB (todo)", None, self.dia_export_db, 0, None ),
- ( "/Debugging/_Import DB (todo)", None, self.dia_import_db, 0, None ),
- ( "/Debugging/_Regression test (todo)", None, self.dia_regression_test, 0, None ),
- ( "/_Help", None, None, 0, "" ),
- ( "/Help/_Main Help", "H", self.tab_main_help, 0, None ),
- ( "/Help/_Abbrevations (todo)", None, self.tab_abbreviations, 0, None ),
- ( "/Help/sep1", None, None, 0, "" ),
- ( "/Help/A_bout (todo)", None, self.dia_about, 0, None ),
- ( "/Help/_License and Copying (todo)", None, self.dia_licensing, 0, None )
- )
-
- self.main_vbox = gtk.VBox(False, 1)
- self.main_vbox.set_border_width(1)
- self.window.add(self.main_vbox)
- self.main_vbox.show()
-
- menubar = self.get_menu(self.window)
- self.main_vbox.pack_start(menubar, False, True, 0)
- menubar.show()
- #done menubar
-
- self.tabs=[]
- self.tab_names=[]
- self.tab_buttons=[]
- self.tab_box = gtk.HBox(False,1)
- self.main_vbox.pack_start(self.tab_box, False, True, 0)
- self.tab_box.show()
- #done tab bar
-
- self.current_tab = gtk.VBox(False,1)
- self.current_tab.set_border_width(1)
- self.main_vbox.add(self.current_tab)
- self.current_tab.show()
-
- self.tab_main_help(None, None)
-
- self.status_bar = gtk.Label("Status: Connected to "+self.db.get_backend_name()+" database named "+self.db.database+" on host "+self.db.host)
- self.main_vbox.pack_end(self.status_bar, False, True, 0)
- self.status_bar.show()
-
- self.window.show()
- #end def __init__
-
- def main(self):
- gtk.main()
- return 0
- #end def main
-
-if __name__ == "__main__":
- me = fpdb()
- me.main()
+#!/usr/bin/python
+
+#Copyright 2008 Steffen Jobbagy-Felso
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU Affero General Public License as published by
+#the Free Software Foundation, version 3 of the License.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU Affero General Public License
+#along with this program. If not, see .
+#In the "official" distribution you can find the license in
+#agpl-3.0.txt in the docs folder of the package.
+
+import os
+import sys
+from optparse import OptionParser
+
+
+parser = OptionParser()
+parser.add_option("-x", "--errorsToConsole", action="store_true",
+ help="If passed error output will go to the console rather than .")
+(options, sys.argv) = parser.parse_args()
+
+if not options.errorsToConsole:
+ print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
+ errorFile = open('fpdb-error-log.txt', 'w', 0)
+ sys.stderr = errorFile
+
+import pygtk
+pygtk.require('2.0')
+import gtk
+
+import fpdb_db
+import fpdb_simple
+import GuiBulkImport
+import GuiPlayerStats
+import GuiTableViewer
+import GuiAutoImport
+import GuiGraphViewer
+import FpdbSQLQueries
+import Configuration
+
+class fpdb:
+ def tab_clicked(self, widget, tab_name):
+ """called when a tab button is clicked to activate that tab"""
+ #print "start of tab_clicked"
+ self.display_tab(tab_name)
+ #end def tab_clicked
+
+ def add_and_display_tab(self, new_tab, new_tab_name):
+ """just calls the component methods"""
+ self.add_tab(new_tab, new_tab_name)
+ self.display_tab(new_tab_name)
+ #end def add_and_display_tab
+
+ def add_tab(self, new_tab, new_tab_name):
+ """adds a tab, namely creates the button and displays it and appends all the relevant arrays"""
+ #print "start of add_tab"
+ for i in self.tab_names: #todo: check this is valid
+ if i==new_tab_name:
+ raise fpdb_simple.FpdbError("duplicate tab_name not permitted")
+
+ self.tabs.append(new_tab)
+ self.tab_names.append(new_tab_name)
+
+ new_tab_sel_button=gtk.ToggleButton(new_tab_name)
+ new_tab_sel_button.connect("clicked", self.tab_clicked, new_tab_name)
+ self.tab_box.add(new_tab_sel_button)
+ new_tab_sel_button.show()
+ self.tab_buttons.append(new_tab_sel_button)
+ #end def add_tab
+
+ def display_tab(self, new_tab_name):
+ """displays the indicated tab"""
+ #print "start of display_tab, len(self.tab_names):",len(self.tab_names)
+ tab_no=-1
+ #if len(self.tab_names)>1:
+ for i in range(len(self.tab_names)):
+ #print "display_tab, new_tab_name:",new_tab_name," self.tab_names[i]:", self.tab_names[i]
+ if (new_tab_name==self.tab_names[i]):
+ tab_no=i
+ #self.tab_buttons[i].set_active(False)
+ #else:
+ # tab_no=0
+
+ #current_tab_no=-1
+ for i in range(len(self.tab_names)):
+ if self.current_tab==self.tabs[i]:
+ #self.tab_buttons[i].set_active(False)
+ pass
+
+ if tab_no==-1:
+ raise fpdb_simple.FpdbError("invalid tab_no")
+ else:
+ self.main_vbox.remove(self.current_tab)
+ #self.current_tab.destroy()
+ self.current_tab=self.tabs[tab_no]
+ self.main_vbox.add(self.current_tab)
+ self.tab_buttons[tab_no].set_active(True)
+ self.current_tab.show()
+ #end def display_tab
+
+ def delete_event(self, widget, event, data=None):
+ return False
+ #end def delete_event
+
+ def destroy(self, widget, data=None):
+ self.quit(widget, data)
+ #end def destroy
+
+ def dia_about(self, widget, data):
+ print "todo: implement dia_about"
+ #end def dia_about
+
+ def dia_create_del_database(self, widget, data):
+ print "todo: implement dia_create_del_database"
+ self.obtain_global_lock()
+ #end def dia_create_del_database
+
+ def dia_create_del_user(self, widget, data):
+ print "todo: implement dia_create_del_user"
+ self.obtain_global_lock()
+ #end def dia_create_del_user
+
+ def dia_database_stats(self, widget, data):
+ print "todo: implement dia_database_stats"
+ #string=fpdb_db.getDbStats(db, cursor)
+ #end def dia_database_stats
+
+ def dia_delete_db_parts(self, widget, data):
+ print "todo: implement dia_delete_db_parts"
+ self.obtain_global_lock()
+ #end def dia_delete_db_parts
+
+ def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
+ print "todo: implement dia_edit_profile"
+ self.obtain_global_lock()
+ #end def dia_edit_profile
+
+ def dia_export_db(self, widget, data):
+ print "todo: implement dia_export_db"
+ self.obtain_global_lock()
+ #end def dia_export_db
+
+ def dia_get_db_root_credentials(self):
+ """obtains db root credentials from user"""
+ print "todo: implement dia_get_db_root_credentials"
+# user, pw=None, None
+#
+# dialog=gtk.Dialog(title="DB Credentials needed", parent=None, flags=0,
+# buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,"Connect and recreate",gtk.RESPONSE_OK))
+#
+# label_warning1=gtk.Label("Please enter credentials for a database user for "+self.host+" that has permissions to create a database.")
+#
+#
+# label_user=gtk.Label("Username")
+# dialog.vbox.add(label_user)
+# label_user.show()
+#
+# response=dialog.run()
+# dialog.destroy()
+# return (user, pw, response)
+ #end def dia_get_db_root_credentials
+
+ def dia_import_db(self, widget, data):
+ print "todo: implement dia_import_db"
+ self.obtain_global_lock()
+ #end def dia_import_db
+
+ def dia_licensing(self, widget, data):
+ print "todo: implement dia_licensing"
+ #end def dia_licensing
+
+ def dia_load_profile(self, widget, data):
+ """Dialogue to select a file to load a profile from"""
+ self.obtain_global_lock()
+ chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
+ chooser.set_filename(self.profile)
+
+ response = chooser.run()
+ chooser.destroy()
+ if response == gtk.RESPONSE_OK:
+ self.load_profile(chooser.get_filename())
+ elif response == gtk.RESPONSE_CANCEL:
+ print 'User cancelled loading profile'
+ #end def dia_load_profile
+
+ def dia_recreate_tables(self, widget, data):
+ """Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
+ self.obtain_global_lock()
+ dia_confirm=gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
+ buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
+ diastring=("Please confirm that you want to (re-)create the tables. If there already are tables in the database "+self.db.database+" on "+self.db.host+" they will be deleted.")
+ dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted
+
+ response=dia_confirm.run()
+ dia_confirm.destroy()
+ if response == gtk.RESPONSE_YES:
+ self.db.recreate_tables()
+ elif response == gtk.RESPONSE_NO:
+ print 'User cancelled recreating tables'
+ #end def dia_recreate_tables
+
+ def dia_regression_test(self, widget, data):
+ print "todo: implement dia_regression_test"
+ self.obtain_global_lock()
+ #end def dia_regression_test
+
+ def dia_save_profile(self, widget, data):
+ print "todo: implement dia_save_profile"
+ #end def dia_save_profile
+
+ def diaSetupWizard(self, path):
+ print "todo: implement setup wizard"
+ print "setup wizard not implemented - please create the default configuration file:", path
+ diaSetupWizard = gtk.Dialog(title="Fatal Error - Config File Missing", parent=None, flags=0, buttons=(gtk.STOCK_QUIT,gtk.RESPONSE_OK))
+
+ label = gtk.Label("Please copy the config file from the docs folder to:")
+ diaSetupWizard.vbox.add(label)
+ label.show()
+
+ label = gtk.Label(path)
+ diaSetupWizard.vbox.add(label)
+ label.show()
+
+ label = gtk.Label("and edit it according to the install documentation at http://fpdb.sourceforge.net")
+ diaSetupWizard.vbox.add(label)
+ label.show()
+
+ response = diaSetupWizard.run()
+ sys.exit(1)
+ #end def diaSetupWizard
+
+ def get_menu(self, window):
+ """returns the menu for this program"""
+ accel_group = gtk.AccelGroup()
+ self.item_factory = gtk.ItemFactory(gtk.MenuBar, "", accel_group)
+ self.item_factory.create_items(self.menu_items)
+ window.add_accel_group(accel_group)
+ return self.item_factory.get_widget("")
+ #end def get_menu
+
+ def load_profile(self):
+ """Loads profile from the provided path name."""
+ self.settings = {}
+ if (os.sep=="/"):
+ self.settings['os']="linuxmac"
+ else:
+ self.settings['os']="windows"
+
+ 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_default_paths())
+
+ if self.db!=None:
+ self.db.disconnect()
+
+ self.db = fpdb_db.fpdb_db()
+ #print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
+ self.db.connect(self.settings['db-backend'],
+ self.settings['db-host'],
+ self.settings['db-databaseName'],
+ self.settings['db-user'],
+ self.settings['db-password'])
+ if self.db.wrongDbVersion:
+ diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
+
+ label = gtk.Label("An invalid DB version or missing tables have been detected.")
+ diaDbVersionWarning.vbox.add(label)
+ label.show()
+
+ label = gtk.Label("This error is not necessarily fatal but it is strongly recommended that you recreate the tables by using the Database menu.")
+ diaDbVersionWarning.vbox.add(label)
+ label.show()
+
+ label = gtk.Label("Not doing this will likely lead to misbehaviour including fpdb crashes, corrupt data etc.")
+ diaDbVersionWarning.vbox.add(label)
+ label.show()
+
+ response = diaDbVersionWarning.run()
+ diaDbVersionWarning.destroy()
+
+ # Database connected to successfully, load queries to pass on to other classes
+ self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
+ #end def load_profile
+
+ def not_implemented(self):
+ print "todo: called unimplemented menu entry (users: pls ignore this)"#remove this once more entries are implemented
+ #end def not_implemented
+
+ def obtain_global_lock(self):
+ print "todo: implement obtain_global_lock (users: pls ignore this)"
+ #end def obtain_global_lock
+
+ def quit(self, widget, data):
+ print "Quitting normally"
+ #check if current settings differ from profile, if so offer to save or abort
+ self.db.disconnect()
+ gtk.main_quit()
+ #end def quit_cliecked
+
+ def release_global_lock(self):
+ print "todo: implement release_global_lock"
+ #end def release_global_lock
+
+ def tab_abbreviations(self, widget, data):
+ print "todo: implement tab_abbreviations"
+ #end def tab_abbreviations
+
+ def tab_auto_import(self, widget, data):
+ """opens the auto import tab"""
+ new_aimp_thread=GuiAutoImport.GuiAutoImport(self.settings, self.config)
+ self.threads.append(new_aimp_thread)
+ aimp_tab=new_aimp_thread.get_vbox()
+ self.add_and_display_tab(aimp_tab, "Auto Import")
+ #end def tab_auto_import
+
+ def tab_bulk_import(self, widget, data):
+ """opens a tab for bulk importing"""
+ #print "start of tab_bulk_import"
+ new_import_thread=GuiBulkImport.GuiBulkImport(self.db, self.settings, self.config)
+ self.threads.append(new_import_thread)
+ bulk_tab=new_import_thread.get_vbox()
+ self.add_and_display_tab(bulk_tab, "Bulk Import")
+ #end def tab_bulk_import
+
+ def tab_player_stats(self, widget, data):
+ new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.db, self.config, self.querydict)
+ self.threads.append(new_ps_thread)
+ ps_tab=new_ps_thread.get_vbox()
+ self.add_and_display_tab(ps_tab, "Player Stats")
+
+
+ def tab_main_help(self, widget, data):
+ """Displays a tab with the main fpdb help screen"""
+ #print "start of tab_main_help"
+ mh_tab=gtk.Label("""Welcome to Fpdb!
+For documentation please visit our website at http://fpdb.sourceforge.net/ or check the docs directory in the fpdb folder.
+Please note that default.conf is no longer needed nor used, all configuration now happens in HUD_config.xml
+This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
+ self.add_and_display_tab(mh_tab, "Help")
+ #end def tab_main_help
+
+ def tab_table_viewer(self, widget, data):
+ """opens a table viewer tab"""
+ #print "start of tab_table_viewer"
+ new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings)
+ self.threads.append(new_tv_thread)
+ tv_tab=new_tv_thread.get_vbox()
+ self.add_and_display_tab(tv_tab, "Table Viewer")
+ #end def tab_table_viewer
+
+ def tabGraphViewer(self, widget, data):
+ """opens a graph viewer tab"""
+ #print "start of tabGraphViewer"
+ new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config)
+ self.threads.append(new_gv_thread)
+ gv_tab=new_gv_thread.get_vbox()
+ self.add_and_display_tab(gv_tab, "Graphs")
+ #end def tabGraphViewer
+
+ def __init__(self):
+ self.threads=[]
+ self.db=None
+ self.config = Configuration.Config()
+ self.load_profile()
+
+ self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.window.connect("delete_event", self.delete_event)
+ self.window.connect("destroy", self.destroy)
+ self.window.set_title("Free Poker DB - version: alpha9+, p143 or higher")
+ self.window.set_border_width(1)
+ self.window.set_size_request(1020,400)
+ self.window.set_resizable(True)
+
+ self.menu_items = (
+ ( "/_Main", None, None, 0, "" ),
+ ( "/Main/_Load Profile (broken)", "L", self.dia_load_profile, 0, None ),
+ ( "/Main/_Edit Profile (todo)", "E", self.dia_edit_profile, 0, None ),
+ ( "/Main/_Save Profile (todo)", None, self.dia_save_profile, 0, None ),
+ ("/Main/sep1", None, None, 0, "" ),
+ ("/Main/_Quit", "Q", self.quit, 0, None ),
+ ("/_Import", None, None, 0, "" ),
+ ("/Import/_Bulk Import", "B", self.tab_bulk_import, 0, None ),
+ ("/Import/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ),
+ ("/Import/Auto _Rating (todo)", "R", self.not_implemented, 0, None ),
+ ("/_Viewers", None, None, 0, "" ),
+ ("/_Viewers/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ),
+ ("/Viewers/_Graphs", "G", self.tabGraphViewer, 0, None ),
+ ("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ),
+ ("/Viewers/Player _Details (todo)", None, self.not_implemented, 0, None ),
+ ("/Viewers/_Player Stats (tabulated view)", None, self.tab_player_stats, 0, None ),
+ ("/Viewers/Starting _Hands (todo)", None, self.not_implemented, 0, None ),
+ ("/Viewers/_Session Replayer (todo)", None, self.not_implemented, 0, None ),
+ ("/Viewers/Poker_table Viewer (mostly obselete)", "T", self.tab_table_viewer, 0, None ),
+ #( "/Viewers/Tourney Replayer
+ ( "/_Database", None, None, 0, "" ),
+ ( "/Database/Create or Delete _Database (todo)", None, self.dia_create_del_database, 0, None ),
+ ( "/Database/Create or Delete _User (todo)", None, self.dia_create_del_user, 0, None ),
+ ( "/Database/Create or Recreate _Tables", None, self.dia_recreate_tables, 0, None ),
+ ( "/Database/_Statistics (todo)", None, self.dia_database_stats, 0, None ),
+ ( "/D_ebugging", None, None, 0, "" ),
+ ( "/Debugging/_Delete Parts of Database (todo)", None, self.dia_delete_db_parts, 0, None ),
+ ( "/Debugging/_Export DB (todo)", None, self.dia_export_db, 0, None ),
+ ( "/Debugging/_Import DB (todo)", None, self.dia_import_db, 0, None ),
+ ( "/Debugging/_Regression test (todo)", None, self.dia_regression_test, 0, None ),
+ ( "/_Help", None, None, 0, "" ),
+ ( "/Help/_Main Help", "H", self.tab_main_help, 0, None ),
+ ( "/Help/_Abbrevations (todo)", None, self.tab_abbreviations, 0, None ),
+ ( "/Help/sep1", None, None, 0, "" ),
+ ( "/Help/A_bout (todo)", None, self.dia_about, 0, None ),
+ ( "/Help/_License and Copying (todo)", None, self.dia_licensing, 0, None )
+ )
+
+ self.main_vbox = gtk.VBox(False, 1)
+ self.main_vbox.set_border_width(1)
+ self.window.add(self.main_vbox)
+ self.main_vbox.show()
+
+ menubar = self.get_menu(self.window)
+ self.main_vbox.pack_start(menubar, False, True, 0)
+ menubar.show()
+ #done menubar
+
+ self.tabs=[]
+ self.tab_names=[]
+ self.tab_buttons=[]
+ self.tab_box = gtk.HBox(False,1)
+ self.main_vbox.pack_start(self.tab_box, False, True, 0)
+ self.tab_box.show()
+ #done tab bar
+
+ self.current_tab = gtk.VBox(False,1)
+ self.current_tab.set_border_width(1)
+ self.main_vbox.add(self.current_tab)
+ self.current_tab.show()
+
+ self.tab_main_help(None, None)
+
+ self.status_bar = gtk.Label("Status: Connected to "+self.db.get_backend_name()+" database named "+self.db.database+" on host "+self.db.host)
+ self.main_vbox.pack_end(self.status_bar, False, True, 0)
+ self.status_bar.show()
+
+ self.window.show()
+ #end def __init__
+
+ def main(self):
+ gtk.main()
+ return 0
+ #end def main
+
+if __name__ == "__main__":
+ me = fpdb()
+ me.main()
diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py
index 1c5c97bb..2663224e 100644
--- a/pyfpdb/fpdb_db.py
+++ b/pyfpdb/fpdb_db.py
@@ -31,26 +31,39 @@ class fpdb_db:
self.SQLITE=4
#end def __init__
- def connect(self, backend, host, database, user, password):
+ def connect(self, backend=None, host=None, database=None,
+ user=None, password=None):
"""Connects a database with the given parameters"""
+ if backend is None:
+ raise FpdbError('Database backend not defined')
self.backend=backend
self.host=host
- self.database=database
self.user=user
self.password=password
+ self.database=database
if backend==self.MYSQL_INNODB:
import MySQLdb
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database)
elif backend==self.PGSQL:
import psycopg2
- self.db = psycopg2.connect(host = host, user = user, password = password, database = database)
+ # If DB connection is made over TCP, then the variables
+ # host, user and password are required
+ if self.host or self.user:
+ self.db = psycopg2.connect(host = host,
+ user = user,
+ password = password,
+ database = database)
+ # For local domain-socket connections, only DB name is
+ # needed, and everything else is in fact undefined and/or
+ # flat out wrong
+ else:
+ self.db = psycopg2.connect(database = database)
else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor()
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
-
# Set up query dictionary as early in the connection process as we can.
- self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
+ self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
self.wrongDbVersion=False
try:
self.cursor.execute("SELECT * FROM Settings")
@@ -82,23 +95,23 @@ class fpdb_db:
def create_tables(self):
#todo: should detect and fail gracefully if tables already exist.
self.cursor.execute(self.sql.query['createSettingsTable'])
- self.cursor.execute(self.sql.query['createSitesTable'])
- self.cursor.execute(self.sql.query['createGametypesTable'])
- self.cursor.execute(self.sql.query['createPlayersTable'])
- self.cursor.execute(self.sql.query['createAutoratesTable'])
- self.cursor.execute(self.sql.query['createHandsTable'])
- self.cursor.execute(self.sql.query['createBoardCardsTable'])
- self.cursor.execute(self.sql.query['createTourneyTypesTable'])
- self.cursor.execute(self.sql.query['createTourneysTable'])
- self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
- self.cursor.execute(self.sql.query['createHandsPlayersTable'])
- self.cursor.execute(self.sql.query['createHandsActionsTable'])
- self.cursor.execute(self.sql.query['createHudCacheTable'])
- self.cursor.execute(self.sql.query['addTourneyIndex'])
- self.cursor.execute(self.sql.query['addHandsIndex'])
- self.cursor.execute(self.sql.query['addPlayersIndex'])
- self.fillDefaultData()
- self.db.commit()
+ self.cursor.execute(self.sql.query['createSitesTable'])
+ self.cursor.execute(self.sql.query['createGametypesTable'])
+ self.cursor.execute(self.sql.query['createPlayersTable'])
+ self.cursor.execute(self.sql.query['createAutoratesTable'])
+ self.cursor.execute(self.sql.query['createHandsTable'])
+ self.cursor.execute(self.sql.query['createBoardCardsTable'])
+ self.cursor.execute(self.sql.query['createTourneyTypesTable'])
+ self.cursor.execute(self.sql.query['createTourneysTable'])
+ self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
+ self.cursor.execute(self.sql.query['createHandsPlayersTable'])
+ self.cursor.execute(self.sql.query['createHandsActionsTable'])
+ self.cursor.execute(self.sql.query['createHudCacheTable'])
+ self.cursor.execute(self.sql.query['addTourneyIndex'])
+ self.cursor.execute(self.sql.query['addHandsIndex'])
+ self.cursor.execute(self.sql.query['addPlayersIndex'])
+ self.fillDefaultData()
+ self.db.commit()
#end def disconnect
def drop_tables(self):
@@ -106,23 +119,23 @@ class fpdb_db:
if(self.get_backend_name() == 'MySQL InnoDB'):
#Databases with FOREIGN KEY support need this switched of before you can drop tables
- self.drop_referencial_integrity()
+ self.drop_referencial_integrity()
# Query the DB to see what tables exist
self.cursor.execute(self.sql.query['list_tables'])
- for table in self.cursor:
- self.cursor.execute(self.sql.query['drop_table'] + table[0])
+ for table in self.cursor:
+ self.cursor.execute(self.sql.query['drop_table'] + table[0])
elif(self.get_backend_name() == 'PostgreSQL'):
self.db.commit()# I have no idea why this makes the query work--REB 07OCT2008
self.cursor.execute(self.sql.query['list_tables'])
tables = self.cursor.fetchall()
- for table in tables:
- self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
+ for table in tables:
+ self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
elif(self.get_backend_name() == 'SQLite'):
#todo: sqlite version here
print "Empty function here"
- self.db.commit()
+ self.db.commit()
#end def drop_tables
def drop_referencial_integrity(self):
diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py
index e2a2779e..fd2a321f 100644
--- a/pyfpdb/fpdb_import.py
+++ b/pyfpdb/fpdb_import.py
@@ -1,310 +1,322 @@
-#!/usr/bin/python
-
-#Copyright 2008 Steffen Jobbagy-Felso
-#This program is free software: you can redistribute it and/or modify
-#it under the terms of the GNU Affero General Public License as published by
-#the Free Software Foundation, version 3 of the License.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU Affero General Public License
-#along with this program. If not, see .
-#In the "official" distribution you can find the license in
-#agpl-3.0.txt in the docs folder of the package.
-
-#see status.txt for site/games support info
-
-import sys
-
-try:
- import MySQLdb
- mysqlLibFound=True
-except:
- pass
-
-try:
- import psycopg2
- pgsqlLibFound=True
-except:
- pass
-
-import math
-import os
-import datetime
-import re
-import fpdb_simple
-import fpdb_parse_logic
-from time import time
-
-class Importer:
-
- def __init__(self, caller, settings, config):
- """Constructor"""
- self.settings=settings
- self.caller=caller
- self.config = config
- self.db = None
- self.cursor = None
- self.filelist = {}
- self.dirlist = {}
- self.monitor = False
- self.updated = {} #Time last import was run {file:mtime}
- self.lines = None
- self.faobs = None #File as one big string
- self.pos_in_file = {} # dict to remember how far we have read in the file
- #Set defaults
- self.callHud = self.config.get_import_parameters().get("callFpdbHud")
- if not self.settings.has_key('minPrint'):
- self.settings['minPrint'] = 30
- self.dbConnect()
-
- def dbConnect(self):
- #connect to DB
- if self.settings['db-backend'] == 2:
- if not mysqlLibFound:
- raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file")
- self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'],
- self.settings['db-password'], self.settings['db-databaseName'])
- elif self.settings['db-backend'] == 3:
- if not pgsqlLibFound:
- raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file")
- print self.settings
- self.db = psycopg2.connect(host = self.settings['db-host'],
- user = self.settings['db-user'],
- password = self.settings['db-password'],
- database = self.settings['db-databaseName'])
- elif self.settings['db-backend'] == 4:
- pass
- else:
- pass
- self.cursor = self.db.cursor()
-
- #Set functions
- def setCallHud(self, value):
- self.callHud = value
-
- def setMinPrint(self, value):
- self.settings['minPrint'] = int(value)
-
- def setHandCount(self, value):
- self.settings['handCount'] = int(value)
-
- def setQuiet(self, value):
- self.settings['quiet'] = value
-
- def setFailOnError(self, value):
- self.settings['failOnError'] = value
-
-# def setWatchTime(self):
-# self.updated = time()
-
- def clearFileList(self):
- self.filelist = {}
-
- #Add an individual file to filelist
- def addImportFile(self, filename, site = "default", filter = "passthrough"):
- #TODO: test it is a valid file
- self.filelist[filename] = [site] + [filter]
-
- #Add a directory of files to filelist
- #Only one import directory per site supported.
- #dirlist is a hash of lists:
- #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
- def addImportDirectory(self, dir, monitor = False, site = "default", filter = "passthrough"):
- if dir != "/dev/null" and dir.lower() != "none":
- if os.path.isdir(dir):
- if monitor == True:
- self.monitor = True
- self.dirlist[site] = [dir] + [filter]
-
- for file in os.listdir(dir):
- self.addImportFile(os.path.join(dir, file), site, filter)
- else:
- print "Warning: Attempted to add: '" + str(dir) + "' as an import directory\n"
-
- #Run full import on filelist
- def runImport(self):
- for file in self.filelist:
- self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
-
- #Run import on updated files, then store latest update time.
- def runUpdated(self):
- #Check for new files in directory
- #todo: make efficient - always checks for new file, should be able to use mtime of directory
- # ^^ May not work on windows
- for site in self.dirlist:
- self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
-
- for file in self.filelist:
- stat_info = os.stat(file)
- try:
- lastupdate = self.updated[file]
- if stat_info.st_mtime > lastupdate:
- self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
- self.updated[file] = time()
- except:
- self.updated[file] = time()
- # This codepath only runs first time the file is found, if modified in the last
- # minute run an immediate import.
- if (time() - stat_info.st_mtime) < 60:
- self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
-
- # This is now an internal function that should not be called directly.
- def import_file_dict(self, file, site, filter):
- if(filter == "passthrough"):
- self.import_fpdb_file(file, site)
- else:
- #Load filter, and run filtered file though main importer
- self.import_fpdb_file(file, site)
-
-
- def import_fpdb_file(self, file, site):
- starttime = time()
- last_read_hand=0
- loc = 0
- if (file=="stdin"):
- inputFile=sys.stdin
- else:
- inputFile=open(file, "rU")
- try: loc = self.pos_in_file[file]
- except: pass
-
- # Read input file into class and close file
- inputFile.seek(loc)
- self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines())
- self.pos_in_file[file] = inputFile.tell()
- inputFile.close()
-
- try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
- firstline = self.lines[0]
- except:
-# print "import_fpdb_file", file, site, self.lines, "\n"
- return
-
- if firstline.find("Tournament Summary")!=-1:
- print "TODO: implement importing tournament summaries"
- #self.faobs = readfile(inputFile)
- #self.parseTourneyHistory()
- return 0
-
- site=fpdb_simple.recogniseSite(firstline)
- category=fpdb_simple.recogniseCategory(firstline)
-
- startpos=0
- stored=0 #counter
- duplicates=0 #counter
- partial=0 #counter
- errors=0 #counter
-
- for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method
- if (len(self.lines[i])<2):
- endpos=i
- hand=self.lines[startpos:endpos]
-
- if (len(hand[0])<2):
- hand=hand[1:]
-
- cancelled=False
- damaged=False
- if (site=="ftp"):
- for i in range (len(hand)):
- if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right?
- cancelled=True
-
- seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line
- if (seat1!=-1):
- if (hand[i].find("Seat ", seat1+3)!=-1):
- damaged=True
-
- if (len(hand)<3):
- pass
- #todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work.
- elif (hand[0].endswith(" (partial)")): #partial hand - do nothing
- partial+=1
- elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and?
- partial+=1
- elif (cancelled or damaged):
- partial+=1
- else: #normal processing
- isTourney=fpdb_simple.isTourney(hand[0])
- if not isTourney:
- fpdb_simple.filterAnteBlindFold(site,hand)
- hand=fpdb_simple.filterCrap(site, hand, isTourney)
- self.hand=hand
-
- try:
- handsId=fpdb_parse_logic.mainParser(self.db, self.cursor, site, category, hand)
- self.db.commit()
-
- stored+=1
- self.db.commit()
- if self.callHud:
- #print "call to HUD here. handsId:",handsId
- #pipe the Hands.id out to the HUD
- self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
- except fpdb_simple.DuplicateError:
- duplicates+=1
- except (ValueError), fe:
- errors+=1
- self.printEmailErrorMessage(errors, file, hand[0])
-
- if (self.settings['failOnError']):
- self.db.commit() #dont remove this, in case hand processing was cancelled.
- raise
- except (fpdb_simple.FpdbError), fe:
- errors+=1
- self.printEmailErrorMessage(errors, file, hand[0])
-
- #fe.printStackTrace() #todo: get stacktrace
- self.db.rollback()
-
- if (self.settings['failOnError']):
- self.db.commit() #dont remove this, in case hand processing was cancelled.
- raise
- if (self.settings['minPrint']!=0):
- if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0):
- print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors
-
- if (self.settings['handCount']!=0):
- if ((stored+duplicates+partial+errors)>=self.settings['handCount']):
- if (not self.settings['quiet']):
- print "quitting due to reaching the amount of hands to be imported"
- print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:",
- partial, "errors:", errors, " time: %5.3f" % (time() - starttime)
- sys.exit(0)
- startpos=endpos
- print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time: %5.3f" % (time() - starttime)
-
- if stored==0:
- if duplicates>0:
- for line_no in range(len(self.lines)):
- if self.lines[line_no].find("Game #")!=-1:
- final_game_line=self.lines[line_no]
- handsId=fpdb_simple.parseSiteHandNo(final_game_line)
- else:
- print "failed to read a single hand from file:", inputFile
- handsId=0
- #todo: this will cause return of an unstored hand number if the last hand was error or partial
- self.db.commit()
- self.handsId=handsId
- return handsId
-#end def import_file_dict
-
- def parseTourneyHistory(self):
- print "Tourney history parser stub"
- #Find tournament boundaries.
- #print self.foabs
-
-
- def printEmailErrorMessage(self, errors, filename, line):
- print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
- print "Filename:", filename
- print "Here is the first line so you can identify it. Please mention that the error was a ValueError:"
- print self.hand[0]
-
-
-if __name__ == "__main__":
- print "CLI for fpdb_import is now available as CliFpdb.py"
+#!/usr/bin/python
+
+#Copyright 2008 Steffen Jobbagy-Felso
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU Affero General Public License as published by
+#the Free Software Foundation, version 3 of the License.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU Affero General Public License
+#along with this program. If not, see .
+#In the "official" distribution you can find the license in
+#agpl-3.0.txt in the docs folder of the package.
+
+#see status.txt for site/games support info
+
+import sys
+
+try:
+ import MySQLdb
+ mysqlLibFound=True
+except:
+ pass
+
+try:
+ import psycopg2
+ pgsqlLibFound=True
+except:
+ pass
+
+import traceback
+import math
+import os
+import datetime
+import re
+import fpdb_simple
+import fpdb_parse_logic
+from time import time
+
+class Importer:
+
+ def __init__(self, caller, settings, config):
+ """Constructor"""
+ self.settings=settings
+ self.caller=caller
+ self.config = config
+ self.db = None
+ self.cursor = None
+ self.filelist = {}
+ self.dirlist = {}
+ self.monitor = False
+ self.updated = {} #Time last import was run {file:mtime}
+ self.lines = None
+ self.faobs = None #File as one big string
+ self.pos_in_file = {} # dict to remember how far we have read in the file
+ #Set defaults
+ self.callHud = self.config.get_import_parameters().get("callFpdbHud")
+ if not self.settings.has_key('minPrint'):
+ self.settings['minPrint'] = 30
+ self.dbConnect()
+
+ # XXX: Why is this here, when fpdb_db.connect() already does the
+ # same?
+ def dbConnect(self):
+ #connect to DB
+ if self.settings['db-backend'] == 2:
+ if not mysqlLibFound:
+ raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file")
+ self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'],
+ self.settings['db-password'], self.settings['db-databaseName'])
+ elif self.settings['db-backend'] == 3:
+ if not pgsqlLibFound:
+ raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file")
+ print self.settings
+ if not self.settings.has_key('db-host') or \
+ not self.settings.has_key('db-user'):
+ self.db = psycopg2.connect(host = self.settings['db-host'],
+ user = self.settings['db-user'],
+ password = self.settings['db-password'],
+ database = self.settings['db-databaseName'])
+ else:
+ dbname = self.settings['db-databaseName']
+ self.db = psycopg2.connect(database = dbname)
+ elif self.settings['db-backend'] == 4:
+ pass
+ else:
+ pass
+ self.cursor = self.db.cursor()
+
+ #Set functions
+ def setCallHud(self, value):
+ self.callHud = value
+
+ def setMinPrint(self, value):
+ self.settings['minPrint'] = int(value)
+
+ def setHandCount(self, value):
+ self.settings['handCount'] = int(value)
+
+ def setQuiet(self, value):
+ self.settings['quiet'] = value
+
+ def setFailOnError(self, value):
+ self.settings['failOnError'] = value
+
+# def setWatchTime(self):
+# self.updated = time()
+
+ def clearFileList(self):
+ self.filelist = {}
+
+ #Add an individual file to filelist
+ def addImportFile(self, filename, site = "default", filter = "passthrough"):
+ #TODO: test it is a valid file
+ self.filelist[filename] = [site] + [filter]
+
+ #Add a directory of files to filelist
+ #Only one import directory per site supported.
+ #dirlist is a hash of lists:
+ #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
+ def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
+ if os.path.isdir(dir):
+ if monitor == True:
+ self.monitor = True
+ self.dirlist[site] = [dir] + [filter]
+
+ for file in os.listdir(dir):
+ self.addImportFile(os.path.join(dir, file), site, filter)
+ else:
+ print "Warning: Attempted to add: '" + str(dir) + "' as an import directory"
+
+ #Run full import on filelist
+ def runImport(self):
+ for file in self.filelist:
+ self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
+
+ #Run import on updated files, then store latest update time.
+ def runUpdated(self):
+ #Check for new files in directory
+ #todo: make efficient - always checks for new file, should be able to use mtime of directory
+ # ^^ May not work on windows
+ for site in self.dirlist:
+ self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
+
+ for file in self.filelist:
+ stat_info = os.stat(file)
+ try:
+ lastupdate = self.updated[file]
+ if stat_info.st_mtime > lastupdate:
+ self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
+ self.updated[file] = time()
+ except:
+ self.updated[file] = time()
+ # This codepath only runs first time the file is found, if modified in the last
+ # minute run an immediate import.
+ if (time() - stat_info.st_mtime) < 60:
+ self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
+
+ # This is now an internal function that should not be called directly.
+ def import_file_dict(self, file, site, filter):
+ if(filter == "passthrough"):
+ self.import_fpdb_file(file, site)
+ else:
+ #Load filter, and run filtered file though main importer
+ self.import_fpdb_file(file, site)
+
+
+ def import_fpdb_file(self, file, site):
+ starttime = time()
+ last_read_hand=0
+ loc = 0
+ if (file=="stdin"):
+ inputFile=sys.stdin
+ else:
+ inputFile=open(file, "rU")
+ try: loc = self.pos_in_file[file]
+ except: pass
+
+ # Read input file into class and close file
+ inputFile.seek(loc)
+ self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines())
+ self.pos_in_file[file] = inputFile.tell()
+ inputFile.close()
+
+ try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
+ firstline = self.lines[0]
+ except:
+# print "import_fpdb_file", file, site, self.lines, "\n"
+ return
+
+ if firstline.find("Tournament Summary")!=-1:
+ print "TODO: implement importing tournament summaries"
+ #self.faobs = readfile(inputFile)
+ #self.parseTourneyHistory()
+ return 0
+
+ site=fpdb_simple.recogniseSite(firstline)
+ category=fpdb_simple.recogniseCategory(firstline)
+
+ startpos=0
+ stored=0 #counter
+ duplicates=0 #counter
+ partial=0 #counter
+ errors=0 #counter
+
+ for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method
+ if (len(self.lines[i])<2):
+ endpos=i
+ hand=self.lines[startpos:endpos]
+
+ if (len(hand[0])<2):
+ hand=hand[1:]
+
+ cancelled=False
+ damaged=False
+ if (site=="ftp"):
+ for i in range (len(hand)):
+ if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right?
+ cancelled=True
+
+ seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line
+ if (seat1!=-1):
+ if (hand[i].find("Seat ", seat1+3)!=-1):
+ damaged=True
+
+ if (len(hand)<3):
+ pass
+ #todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work.
+ elif (hand[0].endswith(" (partial)")): #partial hand - do nothing
+ partial+=1
+ elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and?
+ partial+=1
+ elif (cancelled or damaged):
+ partial+=1
+ else: #normal processing
+ isTourney=fpdb_simple.isTourney(hand[0])
+ if not isTourney:
+ fpdb_simple.filterAnteBlindFold(site,hand)
+ hand=fpdb_simple.filterCrap(site, hand, isTourney)
+ self.hand=hand
+
+ try:
+ handsId=fpdb_parse_logic.mainParser(self.db, self.cursor, site, category, hand)
+ self.db.commit()
+
+ stored+=1
+ self.db.commit()
+ if self.callHud:
+ #print "call to HUD here. handsId:",handsId
+ #pipe the Hands.id out to the HUD
+ self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
+ except fpdb_simple.DuplicateError:
+ duplicates+=1
+ except (ValueError), fe:
+ errors+=1
+ self.printEmailErrorMessage(errors, file, hand)
+
+ if (self.settings['failOnError']):
+ self.db.commit() #dont remove this, in case hand processing was cancelled.
+ raise
+ except (fpdb_simple.FpdbError), fe:
+ errors+=1
+ self.printEmailErrorMessage(errors, file, hand)
+
+ #fe.printStackTrace() #todo: get stacktrace
+ self.db.rollback()
+
+ if (self.settings['failOnError']):
+ self.db.commit() #dont remove this, in case hand processing was cancelled.
+ raise
+ if (self.settings['minPrint']!=0):
+ if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0):
+ print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors
+
+ if (self.settings['handCount']!=0):
+ if ((stored+duplicates+partial+errors)>=self.settings['handCount']):
+ if (not self.settings['quiet']):
+ print "quitting due to reaching the amount of hands to be imported"
+ print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime)
+ sys.exit(0)
+ startpos=endpos
+ print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime)
+
+ if stored==0:
+ if duplicates>0:
+ for line_no in range(len(self.lines)):
+ if self.lines[line_no].find("Game #")!=-1:
+ final_game_line=self.lines[line_no]
+ handsId=fpdb_simple.parseSiteHandNo(final_game_line)
+ else:
+ print "failed to read a single hand from file:", inputFile
+ handsId=0
+ #todo: this will cause return of an unstored hand number if the last hand was error or partial
+ self.db.commit()
+ self.handsId=handsId
+ return handsId
+#end def import_file_dict
+
+ def parseTourneyHistory(self):
+ print "Tourney history parser stub"
+ #Find tournament boundaries.
+ #print self.foabs
+
+
+ def printEmailErrorMessage(self, errors, filename, line):
+ traceback.print_exc(file=sys.stderr)
+ print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
+ print "Filename:", filename
+ print "Here is the first line so you can identify it. Please mention that the error was a ValueError:"
+ print self.hand[0]
+ print "Hand logged to hand-errors.txt"
+ logfile = open('hand-errors.txt', 'a')
+ for s in self.hand:
+ logfile.write(str(s) + "\n")
+ logfile.write("\n")
+ logfile.close()
+
+if __name__ == "__main__":
+ print "CLI for fpdb_import is now available as CliFpdb.py"
diff --git a/pyfpdb/fpdb_parse_logic.py b/pyfpdb/fpdb_parse_logic.py
index 6652ba1f..8400af31 100644
--- a/pyfpdb/fpdb_parse_logic.py
+++ b/pyfpdb/fpdb_parse_logic.py
@@ -42,6 +42,8 @@ def mainParser(db, cursor, site, category, hand):
smallBlindLine=0
for i in range(len(hand)):
if hand[i].find("posts small blind")!=-1 or hand[i].find("posts the small blind")!=-1:
+ if hand[i][-2:] == "$0":
+ continue
smallBlindLine=i
#print "found small blind line:",smallBlindLine
break
diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py
old mode 100644
new mode 100755
index 571223c4..01247651
--- a/pyfpdb/fpdb_simple.py
+++ b/pyfpdb/fpdb_simple.py
@@ -1,2173 +1,2159 @@
-#!/usr/bin/python
-
-#Copyright 2008 Steffen Jobbagy-Felso
-#This program is free software: you can redistribute it and/or modify
-#it under the terms of the GNU Affero General Public License as published by
-#the Free Software Foundation, version 3 of the License.
-#
-#This program is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-#GNU General Public License for more details.
-#
-#You should have received a copy of the GNU Affero General Public License
-#along with this program. If not, see .
-#In the "official" distribution you can find the license in
-#agpl-3.0.txt in the docs folder of the package.
-
-#This file contains simple functions for fpdb
-
-import datetime
-import re
-
-PS=1
-FTP=2
-
-class DuplicateError(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
-
-class FpdbError(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return repr(self.value)
-
-#returns an array of the total money paid. intending to add rebuys/addons here
-def calcPayin(count, buyin, fee):
- result=[]
- for i in range(count):
- result.append (buyin+fee)
- return result
-#end def calcPayin
-
-def checkPositions(positions):
- """verifies that these positions are valid"""
- for i in range (len(positions)):
- pos=positions[i]
- try:#todo: use type recognition instead of error
- if (len(pos)!=1):
- raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+pos) #dont need to str() here
- except TypeError:#->not string->is int->fine
- pass
-
- ### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB
- if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos!=9):
- raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos))
-#end def fpdb_simple.checkPositions
-
-#classifies each line for further processing in later code. Manipulates the passed arrays.
-def classifyLines(hand, category, lineTypes, lineStreets):
- currentStreet="predeal"
- done=False #set this to true once we reach the last relevant line (the summary, except rake, is all repeats)
- for i in range (len(hand)):
- if (done):
- if (hand[i].find("[")==-1 or hand[i].find("mucked [")==-1):
- lineTypes.append("ignore")
- else: #it's storing a mucked card
- lineTypes.append("cards")
- elif (hand[i].startswith("Dealt to")):
- lineTypes.append("cards")
- elif (i==0):
- lineTypes.append("header")
- elif (hand[i].startswith("Seat ") and ((hand[i].find("in chips")!=-1) or (hand[i].find("($")!=-1))):
- lineTypes.append("name")
- elif (isActionLine(hand[i])):
- lineTypes.append("action")
- if (hand[i].find(" posts ")!=-1 or hand[i].find(" posts the ")!=-1):#need to set this here so the "action" of posting blinds is registered properly
- currentStreet="preflop"
- elif (isWinLine(hand[i])):
- lineTypes.append("win")
- elif (hand[i].startswith("Total pot ") and hand[i].find("Rake")!=-1):
- lineTypes.append("rake")
- done=True
- elif (hand[i]=="*** SHOW DOWN ***" or hand[i]=="*** SUMMARY ***"):
- lineTypes.append("ignore")
- #print "in classifyLine, showdown or summary"
- elif (hand[i].find(" antes ")!=-1 or hand[i].find(" posts the ante ")!=-1):
- lineTypes.append("ante")
- elif (hand[i].startswith("*** FLOP *** [")):
- lineTypes.append("cards")
- currentStreet="flop"
- elif (hand[i].startswith("*** TURN *** [")):
- lineTypes.append("cards")
- currentStreet="turn"
- elif (hand[i].startswith("*** RIVER *** [")):
- lineTypes.append("cards")
- currentStreet="river"
- elif (hand[i].startswith("*** 3")):
- lineTypes.append("ignore")
- currentStreet=0
- elif (hand[i].startswith("*** 4")):
- lineTypes.append("ignore")
- currentStreet=1
- elif (hand[i].startswith("*** 5")):
- lineTypes.append("ignore")
- currentStreet=2
- elif (hand[i].startswith("*** 6")):
- lineTypes.append("ignore")
- currentStreet=3
- elif (hand[i].startswith("*** 7") or hand[i]=="*** RIVER ***"):
- lineTypes.append("ignore")
- currentStreet=4
- elif (hand[i].find(" shows [")!=-1):
- lineTypes.append("cards")
- elif (hand[i].startswith("Table '")):
- lineTypes.append("table")
- else:
- raise FpdbError("unrecognised linetype in:"+hand[i])
- lineStreets.append(currentStreet)
-#end def classifyLines
-
-def convert3B4B(site, category, limit_type, actionTypes, actionAmounts):
- """calculates the actual bet amounts in the given amount array and changes it accordingly."""
- for i in range (len(actionTypes)):
- for j in range (len(actionTypes[i])):
- bets=[]
- for k in range (len(actionTypes[i][j])):
- if (actionTypes[i][j][k]=="bet"):
- bets.append((i,j,k))
- if (len(bets)==2):
- #print "len(bets) 2 or higher, need to correct it. bets:",bets,"len:",len(bets)
- amount2=actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]]
- amount1=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]
- actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]]=amount2-amount1
- elif (len(bets)>2):
- fail=True
- #todo: run correction for below
- if (site=="ps" and category=="holdem" and limit_type=="nl" and len(bets)==3):
- fail=False
- if (site=="ftp" and category=="omahahi" and limit_type=="pl" and len(bets)==3):
- fail=False
-
- if fail:
- print "len(bets)>2 in convert3B4B, i didnt think this is possible. i:",i,"j:",j,"k:",k
- print "actionTypes:",actionTypes
- raise FpdbError ("too many bets in convert3B4B")
- #print "actionAmounts postConvert",actionAmounts
-#end def convert3B4B(actionTypes, actionAmounts)
-
-#Corrects the bet amount if the player had to pay blinds
-def convertBlindBet(actionTypes, actionAmounts):
- i=0#setting street to pre-flop
- for j in range (len(actionTypes[i])):#playerloop
- blinds=[]
- bets=[]
- for k in range (len(actionTypes[i][j])):
- if (actionTypes[i][j][k]=="blind"):
- blinds.append((i,j,k))
-
- if (len(blinds)>0 and actionTypes[i][j][k]=="bet"):
- bets.append((i,j,k))
- if (len(bets)==1):
- blind_amount=actionAmounts[blinds[0][0]][blinds[0][1]][blinds[0][2]]
- bet_amount=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]
- actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]=bet_amount-blind_amount
-#end def convertBlindBet
-
-#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details
-#todo: make this use convertCardValuesBoard
-def convertCardValues(arr):
- for i in range (len(arr)):
- for j in range (len(arr[i])):
- if (arr[i][j]=="A"):
- arr[i][j]=14
- elif (arr[i][j]=="K"):
- arr[i][j]=13
- elif (arr[i][j]=="Q"):
- arr[i][j]=12
- elif (arr[i][j]=="J"):
- arr[i][j]=11
- elif (arr[i][j]=="T"):
- arr[i][j]=10
- else:
- arr[i][j]=int(arr[i][j])
-#end def convertCardValues
-
-#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details
-def convertCardValuesBoard(arr):
- for i in range (len(arr)):
- if (arr[i]=="A"):
- arr[i]=14
- elif (arr[i]=="K"):
- arr[i]=13
- elif (arr[i]=="Q"):
- arr[i]=12
- elif (arr[i]=="J"):
- arr[i]=11
- elif (arr[i]=="T"):
- arr[i]=10
- else:
- arr[i]=int(arr[i])
-#end def convertCardValuesBoard
-
-#this creates the 2D/3D arrays. manipulates the passed arrays instead of returning.
-def createArrays(category, seats, card_values, card_suits, antes, winnings, rakes, action_types, allIns, action_amounts, actionNos, actionTypeByNo):
- for i in range(seats):#create second dimension arrays
- tmp=[]
- card_values.append(tmp)
- tmp=[]
- card_suits.append(tmp)
- antes.append(0)
- winnings.append(0)
- rakes.append(0)
-
- if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
- streetCount=4
- else:
- streetCount=5
-
- for i in range(streetCount): #build the first dimension array, for streets
- tmp=[]
- action_types.append(tmp)
- tmp=[]
- allIns.append(tmp)
- tmp=[]
- action_amounts.append(tmp)
- tmp=[]
- actionNos.append(tmp)
- tmp=[]
- actionTypeByNo.append(tmp)
- for j in range (seats): #second dimension arrays: players
- tmp=[]
- action_types[i].append(tmp)
- tmp=[]
- allIns[i].append(tmp)
- tmp=[]
- action_amounts[i].append(tmp)
- tmp=[]
- actionNos[i].append(tmp)
- if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
- pass
- elif (category=="razz" or category=="studhi" or category=="studhilo"):#need to fill card arrays.
- for i in range(seats):
- for j in range (7):
- card_values[i].append(0)
- card_suits[i].append("x")
- else:
- raise FpdbError("invalid category")
-#end def createArrays
-
-def fill_board_cards(board_values, board_suits):
-#fill up the two board card arrays
- while (len(board_values)<5):
- board_values.append(0)
- board_suits.append("x")
-#end def fill_board_cards
-
-def fillCardArrays(player_count, base, category, card_values, card_suits):
- """fills up the two card arrays"""
- if (category=="holdem"):
- cardCount=2
- elif (category=="omahahi" or category=="omahahilo"):
- cardCount=4
- elif base=="stud":
- cardCount=7
- else:
- raise fpdb_simple.FpdbError ("invalid category:", category)
-
- for i in range (player_count):
- while (len(card_values[i])=1):
- raise DuplicateError ("dupl")
-#end isAlreadyInDB
-
-def isRebuyOrAddon(topline):
- """isRebuyOrAddon not implemented yet"""
- return False
-#end def isRebuyOrAddon
-
-#returns whether the passed topline indicates a tournament or not
-def isTourney(topline):
- if (topline.find("Tournament")!=-1):
- return True
- else:
- return False
-#end def isTourney
-
-#returns boolean whether the passed line is a win line
-def isWinLine(line):
- if (line.find("wins the pot")!=-1):
- return True
- elif (line.find("ties for the high pot")!=-1):
- return True
- elif (line.find("ties for the high main pot")!=-1):
- return True
- elif (line.find("ties for the high side pot")!=-1):
- return True
- elif (line.find("ties for the low pot")!=-1):
- return True
- elif (line.find("ties for the low main pot")!=-1):
- return True
- elif (line.find("ties for the low side pot")!=-1):
- return True
- elif (line.find("ties for the main pot")!=-1): #for ftp tied main pot of split pot
- return True
- elif (line.find("ties for the pot")!=-1): #for ftp tie
- return True
- elif (line.find("ties for the side pot")!=-1): #for ftp tied split pots
- return True
- elif (line.find("wins side pot #")!=-1): #for ftp multi split pots
- return True
- elif (line.find("wins the low main pot")!=-1):
- return True
- elif (line.find("wins the low pot")!=-1):
- return True
- elif (line.find("wins the low side pot")!=-1):
- return True
- elif (line.find("wins the high main pot")!=-1):
- return True
- elif (line.find("wins the high pot")!=-1):
- return True
- elif (line.find("wins the high side pot")!=-1):
- return True
- elif (line.find("wins the main pot")!=-1):
- return True
- elif (line.find("wins the side pot")!=-1): #for ftp split pots
- return True
- elif (line.find("collected")!=-1):
- return True
- else:
- return False #not raising error here, any unknown line wouldve been detected in isActionLine already
-#end def isWinLine
-
-#returns the amount of cash/chips put into the put in the given action line
-def parseActionAmount(line, atype, site, isTourney):
- #if (line.endswith(" and is all-in")):
- # line=line[:-14]
- #elif (line.endswith(", and is all in")):
- # line=line[:-15]
-
- if line.endswith(", and is capped"):#ideally we should recognise this as an all-in if category is capXl
- line=line[:-15]
- if line.endswith(" and is capped"):
- line=line[:-14]
-
-
- if (atype=="fold"):
- amount=0
- elif (atype=="check"):
- amount=0
- elif (atype=="unbet" and site=="ftp"):
- pos1=line.find("$")+1
- pos2=line.find(" returned to")
- amount=float2int(line[pos1:pos2])
- elif (atype=="unbet" and site=="ps"):
- #print "ps unbet, line:",line
- pos1=line.find("$")+1
- if pos1==0:
- pos1=line.find("(")+1
- pos2=line.find(")")
- amount=float2int(line[pos1:pos2])
- elif (atype=="bet" and site=="ps" and line.find(": raises $")!=-1 and line.find("to $")!=-1):
- pos=line.find("to $")+4
- amount=float2int(line[pos:])
- else:
- if not isTourney:
- pos=line.rfind("$")+1
- #print "parseActionAmount, line:", line, "line[pos:]:", line[pos:]
- amount=float2int(line[pos:])
- else:
- #print "line:"+line+"EOL"
- pos=line.rfind(" ")+1
- #print "pos:",pos
- #print "pos of 20:", line.find("20")
- amount=int(line[pos:])
-
- if atype=="unbet":
- amount*=-1
- return amount
-#end def parseActionAmount
-
-#doesnt return anything, simply changes the passed arrays action_types and
-# action_amounts. For stud this expects numeric streets (3-7), for
-# holdem/omaha it expects predeal, preflop, flop, turn or river
-def parseActionLine(site, base, isTourney, line, street, playerIDs, names, action_types, allIns, action_amounts, actionNos, actionTypeByNo):
- if (street=="predeal" or street=="preflop"):
- street=0
- elif (street=="flop"):
- street=1
- elif (street=="turn"):
- street=2
- elif (street=="river"):
- street=3
-
- nextActionNo=0
- for player in range(len(actionNos[street])):
- for count in range(len(actionNos[street][player])):
- if actionNos[street][player][count]>=nextActionNo:
- nextActionNo=actionNos[street][player][count]+1
-
- line, allIn=goesAllInOnThisLine(line)
- atype=parseActionType(line)
- playerno=recognisePlayerNo(line, names, atype)
- amount=parseActionAmount(line, atype, site, isTourney)
-
- action_types[street][playerno].append(atype)
- allIns[street][playerno].append(allIn)
- action_amounts[street][playerno].append(amount)
- actionNos[street][playerno].append(nextActionNo)
- tmp=(playerIDs[playerno], atype)
- actionTypeByNo[street].append(tmp)
-#end def parseActionLine
-
-def goesAllInOnThisLine(line):
- """returns whether the player went all-in on this line and removes the all-in text from the line."""
- isAllIn=False
- if (line.endswith(" and is all-in")):
- line=line[:-14]
- isAllIn=True
- elif (line.endswith(", and is all in")):
- line=line[:-15]
- isAllIn=True
- return (line, isAllIn)
-#end def goesAllInOnThisLine
-
-#returns the action type code (see table design) of the given action line
-def parseActionType(line):
- if (line.startswith("Uncalled bet")):
- return "unbet"
- elif (line.endswith("folds")):
- return "fold"
- elif (line.endswith("checks")):
- return "check"
- elif (line.find("calls")!=-1):
- return "call"
- elif (line.find("brings in for")!=-1):
- return "blind"
- elif (line.find("completes it to")!=-1):
- return "bet"
- #todo: what if someone completes instead of bringing in?
- elif (line.find(" posts $")!=-1):
- return "blind"
- elif (line.find(" posts a dead ")!=-1):
- return "blind"
- elif (line.find(": posts small blind ")!=-1):
- return "blind"
- elif (line.find(" posts the small blind of $")!=-1):
- return "blind"
- elif (line.find(": posts big blind ")!=-1):
- return "blind"
- elif (line.find(" posts the big blind of $")!=-1):
- return "blind"
- elif (line.find(": posts small & big blinds $")!=-1):
- return "blind"
- #todo: seperately record voluntary blind payments made to join table out of turn
- elif (line.find("bets")!=-1):
- return "bet"
- elif (line.find("raises")!=-1):
- return "bet"
- else:
- raise FpdbError ("failed to recognise actiontype in parseActionLine in: "+line)
-#end def parseActionType
-
-#parses the ante out of the given line and checks which player paid it, updates antes accordingly.
-def parseAnteLine(line, site, isTourney, names, antes):
- for i in range(len(names)):
- if (line.startswith(names[i].encode("latin-1"))): #found the ante'er
- pos=line.rfind("$")+1
- if not isTourney:
- antes[i]+=float2int(line[pos:])
- else:
- if line.find("all-in")==-1:
- pos=line.rfind(" ")+1
- antes[i]+=int(line[pos:])
- else:
- pos1=line.rfind("ante")+5
- pos2=line.find(" ",pos1)
- antes[i]+=int(line[pos1:pos2])
- #print "parseAnteLine line: ", line, "antes[i]", antes[i], "antes", antes
-#end def parseAntes
-
-#returns the buyin of a tourney in cents
-def parseBuyin(topline):
- pos1=topline.find("$")+1
- pos2=topline.find("+")
- return float2int(topline[pos1:pos2])
-#end def parseBuyin
-
-#parses a card line and changes the passed arrays accordingly
-#todo: reorganise this messy method
-def parseCardLine(site, category, street, line, names, cardValues, cardSuits, boardValues, boardSuits):
- if (line.startswith("Dealt to ") or line.find(" shows [")!=-1 or line.find("mucked [")!=-1):
- playerNo=recognisePlayerNo(line, names, "card") #anything but unbet will be ok for that string
-
- pos=line.rfind("[")+1
- if (category=="holdem"):
- for i in (pos, pos+3):
- cardValues[playerNo].append(line[i:i+1])
- cardSuits[playerNo].append(line[i+1:i+2])
- if (len(cardValues[playerNo])!=2):
- if cardValues[playerNo][0]==cardValues[playerNo][2] and cardSuits[playerNo][1]==cardSuits[playerNo][3]: #two tests will do
- cardValues[playerNo]=cardValues[playerNo][0:2]
- cardSuits[playerNo]=cardSuits[playerNo][0:2]
- else:
- print "line:",line,"cardValues[playerNo]:",cardValues[playerNo]
- raise FpdbError("read too many/too few holecards in parseCardLine")
- elif (category=="omahahi" or category=="omahahilo"):
- for i in (pos, pos+3, pos+6, pos+9):
- cardValues[playerNo].append(line[i:i+1])
- cardSuits[playerNo].append(line[i+1:i+2])
- if (len(cardValues[playerNo])!=4):
- if cardValues[playerNo][0]==cardValues[playerNo][4] and cardSuits[playerNo][3]==cardSuits[playerNo][7]: #two tests will do
- cardValues[playerNo]=cardValues[playerNo][0:4]
- cardSuits[playerNo]=cardSuits[playerNo][0:4]
- else:
- print "line:",line,"cardValues[playerNo]:",cardValues[playerNo]
- raise FpdbError("read too many/too few holecards in parseCardLine")
- elif (category=="razz" or category=="studhi" or category=="studhilo"):
- if (line.find("shows")==-1):
- #print "parseCardLine(in stud if), street:", street
- if line[pos+2]=="]": #-> not (hero and 3rd street)
- cardValues[playerNo][street+2]=line[pos:pos+1]
- cardSuits[playerNo][street+2]=line[pos+1:pos+2]
- else:
- #print "hero card1:", line[pos:pos+2], "hero card2:", line[pos+3:pos+5], "hero card3:", line[pos+6:pos+8],
- cardValues[playerNo][street]=line[pos:pos+1]
- cardSuits[playerNo][street]=line[pos+1:pos+2]
- cardValues[playerNo][street+1]=line[pos+3:pos+4]
- cardSuits[playerNo][street+1]=line[pos+4:pos+5]
- cardValues[playerNo][street+2]=line[pos+6:pos+7]
- cardSuits[playerNo][street+2]=line[pos+7:pos+8]
- else:
- #print "parseCardLine(in stud else), street:", street
- cardValues[playerNo][0]=line[pos:pos+1]
- cardSuits[playerNo][0]=line[pos+1:pos+2]
- pos+=3
- cardValues[playerNo][1]=line[pos:pos+1]
- cardSuits[playerNo][1]=line[pos+1:pos+2]
- if street==4:
- pos=pos=line.rfind("]")-2
- cardValues[playerNo][6]=line[pos:pos+1]
- cardSuits[playerNo][6]=line[pos+1:pos+2]
- #print "cardValues:", cardValues
- #print "cardSuits:", cardSuits
- else:
- print "line:",line,"street:",street
- raise FpdbError("invalid category")
- #print "end of parseCardLine/playercards, cardValues:",cardValues
- elif (line.startswith("*** FLOP ***")):
- pos=line.find("[")+1
- for i in (pos, pos+3, pos+6):
- boardValues.append(line[i:i+1])
- boardSuits.append(line[i+1:i+2])
- #print boardValues
- elif (line.startswith("*** TURN ***") or line.startswith("*** RIVER ***")):
- pos=line.find("[")+1
- pos=line.find("[", pos+1)+1
- boardValues.append(line[pos:pos+1])
- boardSuits.append(line[pos+1:pos+2])
- #print boardValues
- else:
- raise FpdbError ("unrecognised line:"+line)
-#end def parseCardLine
-
-def parseCashesAndSeatNos(lines, site):
- """parses the startCashes and seatNos of each player out of the given lines and returns them as a dictionary of two arrays"""
- cashes = []
- seatNos = []
- for i in range (len(lines)):
- pos2=lines[i].find(":")
- seatNos.append(int(lines[i][5:pos2]))
-
- pos1=lines[i].rfind("($")+2
- if pos1==1: #for tourneys - it's 1 instead of -1 due to adding 2 above
- pos1=lines[i].rfind("(")+1
- if (site=="ftp"):
- pos2=lines[i].rfind(")")
- elif (site=="ps"):
- pos2=lines[i].find(" in chips")
- cashes.append(float2int(lines[i][pos1:pos2]))
- return {'startCashes':cashes, 'seatNos':seatNos}
-#end def parseCashesAndSeatNos
-
-#returns the buyin of a tourney in cents
-def parseFee(topline):
- pos1=topline.find("$")+1
- pos1=topline.find("$",pos1)+1
- pos2=topline.find(" ", pos1)
- return float2int(topline[pos1:pos2])
-#end def parsefee
-
-#returns a datetime object with the starttime indicated in the given topline
-def parseHandStartTime(topline, site):
- #convert x:13:35 to 0x:13:35
- counter=0
- while (True):
- pos=topline.find(" "+str(counter)+":")
- if (pos!=-1):
- topline=topline[0:pos+1]+"0"+topline[pos+1:]
- counter+=1
- if counter==10: break
-
- isUTC=False
- if site=="ftp":
- pos = topline.find(" ", len(topline)-26)+1
- tmp = topline[pos:]
- #print "year:", tmp[14:18], "month", tmp[19:21], "day", tmp[22:24], "hour", tmp[0:2], "minute", tmp[3:5], "second", tmp[6:8]
- result = datetime.datetime(int(tmp[14:18]), int(tmp[19:21]), int(tmp[22:24]), int(tmp[0:2]), int(tmp[3:5]), int(tmp[6:8]))
- elif site=="ps":
- if topline.find("UTC")!=-1:
- pos1 = topline.find("-")+2
- pos2 = topline.find("UTC")
- tmp=topline[pos1:pos2]
- isUTC=True
- else:
- tmp=topline[-30:]
- #print "parsehandStartTime, tmp:", tmp
- pos = tmp.find("-")+2
- tmp = tmp[pos:]
- #Need to match either
- # 2008/09/07 06:23:14 ET or
- # 2008/08/17 - 01:14:43 (ET)
- m = re.match('(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P [0-9]{2}):(?P[0-9]{2}):(?P[0-9]{2})',tmp)
- #print "year:", int(m.group('YEAR')), "month", int(m.group('MON')), "day", int(m.group('DAY')), "hour", int(m.group('HR')), "minute", int(m.group('MIN')), "second", int(m.group('SEC'))
- result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
- else:
- raise FpdbError("invalid site in parseHandStartTime")
-
- if (site=="ftp" or site=="ps") and not isUTC: #these use US ET
- result+=datetime.timedelta(hours=5)
-
- return result
-#end def parseHandStartTime
-
-#parses the names out of the given lines and returns them as an array
-def parseNames(lines):
- result = []
- for i in range (len(lines)):
- pos1=lines[i].find(":")+2
- pos2=lines[i].rfind("(")-1
- tmp=lines[i][pos1:pos2]
- #print "parseNames, tmp original:",tmp
- tmp=unicode(tmp,"latin-1")
- #print "parseNames, tmp after unicode latin-1 conversion:",tmp
- result.append(tmp)
- return result
-#end def parseNames
-
-#returns an array with the positions of the respective players
-def parsePositions (hand, names):
- #prep array
- positions=[]
- for i in range(len(names)):
- positions.append(-1)
-
- #find blinds
- sb,bb=-1,-1
- for i in range (len(hand)):
- if (sb==-1 and hand[i].find("small blind")!=-1 and hand[i].find("dead small blind")==-1):
- sb=hand[i]
- #print "sb:",sb
- if (bb==-1 and hand[i].find("big blind")!=-1 and hand[i].find("dead big blind")==-1):
- bb=hand[i]
- #print "bb:",bb
-
- #identify blinds
- #print "parsePositions before recognising sb/bb. names:",names
- sbExists=True
- if (sb!=-1):
- sb=recognisePlayerNo(sb, names, "bet")
- else:
- sbExists=False
- if (bb!=-1):
- bb=recognisePlayerNo(bb, names, "bet")
-
- #write blinds into array
- if (sbExists):
- positions[sb]="S"
- positions[bb]="B"
-
- #fill up rest of array
- if (sbExists):
- arraypos=sb-1
- else:
- arraypos=bb-1
- distFromBtn=0
- while (arraypos>=0 and arraypos != bb):
- #print "parsePositions first while, arraypos:",arraypos,"positions:",positions
- positions[arraypos]=distFromBtn
- arraypos-=1
- distFromBtn+=1
-
- ### RHH - Changed to set the null seats before BB to "9"
- i=bb-1
- while positions[i] < 0:
- positions[i]=9
- i-=1
-
- arraypos=len(names)-1
- if (bb!=0 or (bb==0 and sbExists==False)):
- while (arraypos>bb):
- positions[arraypos]=distFromBtn
- arraypos-=1
- distFromBtn+=1
-
- for i in range (len(names)):
- if positions[i]==-1:
- print "parsePositions names:",names
- print "result:",positions
- raise FpdbError ("failed to read positions")
- return positions
-#end def parsePositions
-
-#simply parses the rake amount and returns it as an int
-def parseRake(line):
- pos=line.find("Rake")+6
- rake=float2int(line[pos:])
- return rake
-#end def parseRake
-
-def parseSiteHandNo(topline):
- """returns the hand no assigned by the poker site"""
- pos1=topline.find("#")+1
- pos2=topline.find(":")
- return topline[pos1:pos2]
-#end def parseSiteHandNo
-
-def parseTableLine(site, base, line):
- """returns a dictionary with maxSeats and tableName"""
- if site=="ps":
- pos1=line.find('\'')+1
- pos2=line.find('\'', pos1)
- #print "table:",line[pos1:pos2]
- pos3=pos2+2
- pos4=line.find("-max")
- #print "seats:",line[pos3:pos4]
- return {'maxSeats':int(line[pos3:pos4]), 'tableName':line[pos1:pos2]}
- elif site=="ftp":
- pos1=line.find("Table ")+6
- pos2=line.find("-")-1
- if base=="hold":
- maxSeats=9
- elif base=="stud":
- maxSeats=8
-
- if line.find("6 max")!=-1:
- maxSeats=6
- elif line.find("4 max")!=-1:
- maxSeats=4
- elif line.find("heads up")!=-1:
- maxSeats=2
-
- return {'maxSeats':maxSeats, 'tableName':line[pos1:pos2]}
- else:
- raise FpdbError("invalid site ID")
-#end def parseTableLine
-
-#returns the hand no assigned by the poker site
-def parseTourneyNo(topline):
- pos1=topline.find("Tournament #")+12
- pos2=topline.find(",", pos1)
- #print "parseTourneyNo pos1:",pos1," pos2:",pos2, " result:",topline[pos1:pos2]
- return topline[pos1:pos2]
-#end def parseTourneyNo
-
-#parses a win/collect line. manipulates the passed array winnings, no explicit return
-def parseWinLine(line, site, names, winnings, isTourney):
- #print "parseWinLine: line:",line
- for i in range(len(names)):
- if (line.startswith(names[i].encode("latin-1"))): #found a winner
- if isTourney:
- pos1=line.rfind("collected ")+10
- if (site=="ftp"):
- pos2=line.find(")", pos1)
- elif (site=="ps"):
- pos2=line.find(" ", pos1)
- winnings[i]+=int(line[pos1:pos2])
- else:
- pos1=line.rfind("$")+1
- if (site=="ftp"):
- pos2=line.find(")", pos1)
- elif (site=="ps"):
- pos2=line.find(" ", pos1)
- winnings[i]+=float2int(line[pos1:pos2])
-#end def parseWinLine
-
-#returns the category (as per database) string for the given line
-def recogniseCategory(line):
- if (line.find("Razz")!=-1):
- return "razz"
- elif (line.find("Hold'em")!=-1):
- return "holdem"
- elif (line.find("Omaha")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1):
- return "omahahi"
- elif (line.find("Omaha")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)):
- return "omahahilo"
- elif (line.find("Stud")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1):
- return "studhi"
- elif (line.find("Stud")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)):
- return "studhilo"
- else:
- raise FpdbError("failed to recognise category, line:"+line)
-#end def recogniseCategory
-
-#returns the int for the gametype_id for the given line
-def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy
- #if (topline.find("HORSE")!=-1):
- # raise FpdbError("recogniseGametypeID: HORSE is not yet supported.")
-
- #note: the below variable names small_bet and big_bet are misleading, in NL/PL they mean small/big blind
- if isTourney:
- type="tour"
- pos1=topline.find("(")+1
- if (topline[pos1]=="H" or topline[pos1]=="O" or topline[pos1]=="R" or topline[pos1]=="S" or topline[pos1+2]=="C"):
- pos1=topline.find("(", pos1)+1
- pos2=topline.find("/", pos1)
- small_bet=int(topline[pos1:pos2])
- else:
- type="ring"
- pos1=topline.find("$")+1
- pos2=topline.find("/$")
- small_bet=float2int(topline[pos1:pos2])
-
- pos1=pos2+2
- if isTourney:
- pos1-=1
- if (site_id==1): #ftp
- pos2=topline.find(" ", pos1)
- elif (site_id==2): #ps
- pos2=topline.find(")")
-
- if pos2<=pos1:
- pos2=topline.find(")", pos1)
-
- if isTourney:
- big_bet=int(topline[pos1:pos2])
- else:
- big_bet=float2int(topline[pos1:pos2])
-
- if (topline.find("No Limit")!=-1):
- limit_type="nl"
- if (topline.find("Cap No")!=-1):
- limit_type="cn"
- elif (topline.find("Pot Limit")!=-1):
- limit_type="pl"
- if (topline.find("Cap Pot")!=-1):
- limit_type="cp"
- else:
- limit_type="fl"
-
- #print "recogniseGametypeID small_bet/blind:",small_bet,"big bet/blind:", big_bet,"limit type:",limit_type
- if (limit_type=="fl"):
- cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
- else:
- cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
- result=cursor.fetchone()
- #print "recgt1 result=",result
- #ret=result[0]
- #print "recgt1 ret=",ret
- #print "tried SELECTing gametypes.id, result:",result
-
- try:
- len(result)
- except TypeError:
- if category=="holdem" or category=="omahahi" or category=="omahahilo":
- base="hold"
- else:
- base="stud"
-
- if category=="holdem" or category=="omahahi" or category=="studhi":
- hiLo='h'
- elif category=="razz":
- hiLo='l'
- else:
- hiLo='s'
-
- if (limit_type=="fl"):
- big_blind=small_bet
- if base=="hold":
- if smallBlindLine==topline:
- raise FpdbError("invalid small blind line")
- elif isTourney:
- pos=smallBlindLine.rfind(" ")+1
- small_blind=int(smallBlindLine[pos:])
- else:
- pos=smallBlindLine.rfind("$")+1
- small_blind=float2int(smallBlindLine[pos:])
- else:
- small_blind=0
- cursor.execute("""INSERT INTO Gametypes
- (siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_blind, big_blind, small_bet, big_bet))
- #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
- else:
- cursor.execute("""INSERT INTO Gametypes
- (siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_bet, big_bet, 0, 0))#remember, for these bet means blind
- #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
-
- result=(db.insert_id(),)
- #print "recgt2 result=",result
- #print "created new gametypes.id:",result
-
- #print "recgt3: result=", result
- return result[0]
-#end def recogniseGametypeID
-
-def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon):
- cursor.execute ("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
- result=cursor.fetchone()
- #print "tried SELECTing gametypes.id, result:",result
-
- try:
- len(result)
- except TypeError:#this means we need to create a new entry
- cursor.execute("""INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) VALUES (%s, %s, %s, %s, %s)""", (siteId, buyin, fee, knockout, rebuyOrAddon))
- cursor.execute("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
- result=cursor.fetchone()
- return result[0]
-#end def recogniseTourneyTypeId
-
-#returns the SQL ids of the names given in an array
-def recognisePlayerIDs(cursor, names, site_id):
- result = []
- for i in range (len(names)):
- cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],))
- tmp=cursor.fetchall()
- if (len(tmp)==0): #new player
- cursor.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (names[i], site_id))
- #print "Number of players rows inserted: %d" % cursor.rowcount
- cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],))
- tmp=cursor.fetchall()
- #print "recognisePlayerIDs, names[i]:",names[i],"tmp:",tmp
- result.append(tmp[0][0])
- return result
-#end def recognisePlayerIDs
-
-#recognises the name in the given line and returns its array position in the given array
-def recognisePlayerNo(line, names, atype):
- #print "recogniseplayerno, names:",names
- for i in range (len(names)):
- if (atype=="unbet"):
- if (line.endswith(names[i].encode("latin-1"))):
- return (i)
- elif (line.startswith("Dealt to ")):
- #print "recognisePlayerNo, card precut, line:",line
- tmp=line[9:]
- #print "recognisePlayerNo, card postcut, tmp:",tmp
- if (tmp.startswith(names[i].encode("latin-1"))):
- return (i)
- elif (line.startswith("Seat ")):
- if (line.startswith("Seat 10")):
- tmp=line[9:]
- else:
- tmp=line[8:]
-
- if (tmp.startswith(names[i].encode("latin-1"))):
- return (i)
- else:
- if (line.startswith(names[i].encode("latin-1"))):
- return (i)
- #if we're here we mustve failed
- raise FpdbError ("failed to recognise player in: "+line+" atype:"+atype)
-#end def recognisePlayerNo
-
-#returns the site abbreviation for the given site
-def recogniseSite(line):
- if (line.startswith("Full Tilt Poker")):
- return "ftp"
- elif (line.startswith("PokerStars")):
- return "ps"
- else:
- raise FpdbError("failed to recognise site, line:"+line)
-#end def recogniseSite
-
-#returns the ID of the given site
-def recogniseSiteID(cursor, site):
- if (site=="ftp"):
- return 1
- #cursor.execute("SELECT id FROM Sites WHERE name = ('Full Tilt Poker')")
- elif (site=="ps"):
- return 2
- #cursor.execute("SELECT id FROM Sites WHERE name = ('PokerStars')")
- else:
- raise FpdbError("invalid site in recogniseSiteID: "+site)
- return cursor.fetchall()[0][0]
-#end def recogniseSiteID
-
-#removes trailing \n from the given array
-def removeTrailingEOL(arr):
- for i in range(len(arr)):
- if (arr[i].endswith("\n")):
- #print "arr[i] before removetrailingEOL:", arr[i]
- arr[i]=arr[i][:-1]
- #print "arr[i] after removetrailingEOL:", arr[i]
- return arr
-#end def removeTrailingEOL
-
-#splits the rake according to the proportion of pot won. manipulates the second passed array.
-def splitRake(winnings, rakes, totalRake):
- winnercnt=0
- totalWin=0
- for i in range(len(winnings)):
- if winnings[i]!=0:
- winnercnt+=1
- totalWin+=winnings[i]
- firstWinner=i
- if winnercnt==1:
- rakes[firstWinner]=totalRake
- else:
- totalWin=float(totalWin)
- for i in range(len(winnings)):
- if winnings[i]!=0:
- winPortion=winnings[i]/totalWin
- rakes[i]=totalRake*winPortion
-#end def splitRake
-
-def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, actionNos):
-#stores into table hands_actions
- #print "start of storeActions, actionNos:",actionNos
- #print " action_amounts:",action_amounts
- for i in range (len(actionTypes)): #iterate through streets
- for j in range (len(actionTypes[i])): #iterate through names
- for k in range (len(actionTypes[i][j])): #iterate through individual actions of that player on that street
- cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)"
- , (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k]))
-#end def storeActions
-
-def store_board_cards(cursor, hands_id, board_values, board_suits):
-#stores into table board_cards
- cursor.execute ("""INSERT INTO BoardCards (handId, card1Value, card1Suit,
- card2Value, card2Suit, card3Value, card3Suit, card4Value, card4Suit,
- card5Value, card5Suit) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
- (hands_id, board_values[0], board_suits[0], board_values[1], board_suits[1],
- board_values[2], board_suits[2], board_values[3], board_suits[3],
- board_values[4], board_suits[4]))
-#end def store_board_cards
-
-def storeHands(db, cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats):
-#stores into table hands
- cursor.execute ("INSERT INTO Hands (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats) VALUES (%s, %s, %s, %s, %s, %s, %s)", (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats))
- #todo: find a better way of doing this...
- #cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id))
- #return cursor.fetchall()[0][0]
- return db.insert_id() # mysql only
-#end def storeHands
-
-def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos):
- result=[]
- if (category=="holdem"):
- for i in range (len(player_ids)):
- cursor.execute ("""
- INSERT INTO HandsPlayers
- (handId, playerId, startCash, position,
- card1Value, card1Suit, card2Value, card2Suit, winnings, rake, seatNo)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
- (hands_id, player_ids[i], start_cashes[i], positions[i],
- card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
- winnings[i], rakes[i], seatNos[i]))
- #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i]))
- #result.append(cursor.fetchall()[0][0])
- result.append( db.insert_id() ) # mysql only
- elif (category=="omahahi" or category=="omahahilo"):
- for i in range (len(player_ids)):
- cursor.execute ("""INSERT INTO HandsPlayers
- (handId, playerId, startCash, position,
- card1Value, card1Suit, card2Value, card2Suit,
- card3Value, card3Suit, card4Value, card4Suit, winnings, rake, seatNo)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
- (hands_id, player_ids[i], start_cashes[i], positions[i],
- card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
- card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
- winnings[i], rakes[i], seatNos[i]))
- #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
- #result.append(cursor.fetchall()[0][0])
- result.append( db.insert_id() ) # mysql only
- else:
- raise FpdbError("invalid category")
- return result
-#end def store_hands_players_holdem_omaha
-
-def store_hands_players_stud(db, cursor, hands_id, player_ids, start_cashes, antes,
- card_values, card_suits, winnings, rakes, seatNos):
-#stores hands_players rows for stud/razz games. returns an array of the resulting IDs
- result=[]
- #print "before inserts in store_hands_players_stud, antes:", antes
- for i in range (len(player_ids)):
- cursor.execute ("""INSERT INTO HandsPlayers
- (handId, playerId, startCash, ante,
- card1Value, card1Suit, card2Value, card2Suit,
- card3Value, card3Suit, card4Value, card4Suit,
- card5Value, card5Suit, card6Value, card6Suit,
- card7Value, card7Suit, winnings, rake, seatNo)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
- %s, %s, %s, %s)""",
- (hands_id, player_ids[i], start_cashes[i], antes[i],
- card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
- card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
- card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
- card_values[i][6], card_suits[i][6], winnings[i], rakes[i], seatNos[i]))
- #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
- #result.append(cursor.fetchall()[0][0])
- result.append( db.insert_id() ) # mysql only
- return result
-#end def store_hands_players_stud
-
-def store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, player_ids,
- start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
-#stores hands_players for tourney holdem/omaha hands
- result=[]
- for i in range (len(player_ids)):
- if len(card_values[0])==2:
- cursor.execute ("""INSERT INTO HandsPlayers
- (handId, playerId, startCash, position,
- card1Value, card1Suit, card2Value, card2Suit,
- winnings, rake, tourneysPlayersId, seatNo)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
- (hands_id, player_ids[i], start_cashes[i], positions[i],
- card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
- winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
- elif len(card_values[0])==4:
- cursor.execute ("""INSERT INTO HandsPlayers
- (handId, playerId, startCash, position,
- card1Value, card1Suit, card2Value, card2Suit,
- card3Value, card3Suit, card4Value, card4Suit,
- winnings, rake, tourneysPlayersId, seatNo)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
- (hands_id, player_ids[i], start_cashes[i], positions[i],
- card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
- card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
- winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
- else:
- raise FpdbError ("invalid card_values length:"+str(len(card_values[0])))
- #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
- #result.append(cursor.fetchall()[0][0])
- result.append( db.insert_id() ) # mysql only
-
- return result
-#end def store_hands_players_holdem_omaha_tourney
-
-def store_hands_players_stud_tourney(db, cursor, hands_id, player_ids, start_cashes,
- antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
-#stores hands_players for tourney stud/razz hands
- result=[]
- for i in range (len(player_ids)):
- cursor.execute ("""INSERT INTO HandsPlayers
- (handId, playerId, startCash, ante,
- card1Value, card1Suit, card2Value, card2Suit,
- card3Value, card3Suit, card4Value, card4Suit,
- card5Value, card5Suit, card6Value, card6Suit,
- card7Value, card7Suit, winnings, rake, tourneysPlayersId, seatNo)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s, %s)""",
- (hands_id, player_ids[i], start_cashes[i], antes[i],
- card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
- card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
- card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
- card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
- #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
- #result.append(cursor.fetchall()[0][0])
- result.append( db.insert_id() ) # mysql only
- return result
-#end def store_hands_players_stud_tourney
-
-def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo
- ,winnings, totalWinnings, positions, actionTypes, actionAmounts):
- """calculates data for the HUD during import. IMPORTANT: if you change this method make
- sure to also change the following storage method and table_viewer.prepare_data if necessary
- """
- #print "generateHudCacheData, len(player_ids)=", len(player_ids)
- #setup subarrays of the result dictionary.
- street0VPI=[]
- street0Aggr=[]
- street0_3B4BChance=[]
- street0_3B4BDone=[]
- street1Seen=[]
- street2Seen=[]
- street3Seen=[]
- street4Seen=[]
- sawShowdown=[]
- street1Aggr=[]
- street2Aggr=[]
- street3Aggr=[]
- street4Aggr=[]
- otherRaisedStreet1=[]
- otherRaisedStreet2=[]
- otherRaisedStreet3=[]
- otherRaisedStreet4=[]
- foldToOtherRaisedStreet1=[]
- foldToOtherRaisedStreet2=[]
- foldToOtherRaisedStreet3=[]
- foldToOtherRaisedStreet4=[]
- wonWhenSeenStreet1=[]
-
- wonAtSD=[]
- stealAttemptChance=[]
- stealAttempted=[]
- hudDataPositions=[]
-
- firstPfRaiseByNo=-1
- firstPfRaiserId=-1
- firstPfRaiserNo=-1
- firstPfCallByNo=-1
- firstPfCallerId=-1
- for i in range(len(actionTypeByNo[0])):
- if actionTypeByNo[0][i][1]=="bet":
- firstPfRaiseByNo=i
- firstPfRaiserId=actionTypeByNo[0][i][0]
- for j in range(len(player_ids)):
- if player_ids[j]==firstPfRaiserId:
- firstPfRaiserNo=j
- break
- break
- for i in range(len(actionTypeByNo[0])):
- if actionTypeByNo[0][i][1]=="call":
- firstPfCallByNo=i
- firstPfCallerId=actionTypeByNo[0][i][0]
- break
-
- cutoffId=-1
- buttonId=-1
- sbId=-1
- bbId=-1
- if base=="hold":
- for player in range(len(positions)):
- if positions==1:
- cutoffId=player_ids[player]
- if positions==0:
- buttonId=player_ids[player]
- if positions=='S':
- sbId=player_ids[player]
- if positions=='B':
- bbId=player_ids[player]
-
- someoneStole=False
-
- #run a loop for each player preparing the actual values that will be commited to SQL
- for player in range (len(player_ids)):
- #set default values
- myStreet0VPI=False
- myStreet0Aggr=False
- myStreet0_3B4BChance=False
- myStreet0_3B4BDone=False
- myStreet1Seen=False
- myStreet2Seen=False
- myStreet3Seen=False
- myStreet4Seen=False
- mySawShowdown=False
- myStreet1Aggr=False
- myStreet2Aggr=False
- myStreet3Aggr=False
- myStreet4Aggr=False
- myOtherRaisedStreet1=False
- myOtherRaisedStreet2=False
- myOtherRaisedStreet3=False
- myOtherRaisedStreet4=False
- myFoldToOtherRaisedStreet1=False
- myFoldToOtherRaisedStreet2=False
- myFoldToOtherRaisedStreet3=False
- myFoldToOtherRaisedStreet4=False
- myWonWhenSeenStreet1=0.0
- myWonAtSD=0.0
- myStealAttemptChance=False
- myStealAttempted=False
-
- #calculate VPIP and PFR
- street=0
- heroPfRaiseCount=0
- for count in range (len(action_types[street][player])):#finally individual actions
- currentAction=action_types[street][player][count]
- if currentAction=="bet":
- myStreet0Aggr=True
- if (currentAction=="bet" or currentAction=="call"):
- myStreet0VPI=True
-
- #PF3B4BChance and PF3B4B
- pfFold=-1
- pfRaise=-1
- if firstPfRaiseByNo!=-1:
- for i in range(len(actionTypeByNo[0])):
- if actionTypeByNo[0][i][0]==player_ids[player]:
- if actionTypeByNo[0][i][1]=="bet" and pfRaise==-1 and i>firstPfRaiseByNo:
- pfRaise=i
- if actionTypeByNo[0][i][1]=="fold" and pfFold==-1:
- pfFold=i
- if pfFold==-1 or pfFold>firstPfRaiseByNo:
- myStreet0_3B4BChance=True
- if pfRaise>firstPfRaiseByNo:
- myStreet0_3B4BDone=True
-
- #steal calculations
- if base=="hold":
- if len(player_ids)>=5: #no point otherwise
- if positions[player]==1:
- if firstPfRaiserId==player_ids[player]:
- myStealAttemptChance=True
- myStealAttempted=True
- elif firstPfRaiserId==buttonId or firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1:
- myStealAttemptChance=True
- if positions[player]==0:
- if firstPfRaiserId==player_ids[player]:
- myStealAttemptChance=True
- myStealAttempted=True
- elif firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1:
- myStealAttemptChance=True
- if positions[player]=='S':
- if firstPfRaiserId==player_ids[player]:
- myStealAttemptChance=True
- myStealAttempted=True
- elif firstPfRaiserId==bbId or firstPfRaiserId==-1:
- myStealAttemptChance=True
- if positions[player]=='B':
- pass
-
- if myStealAttempted:
- someoneStole=True
-
-
- #calculate saw* values
- isAllIn=False
- for i in range(len(allIns[0][player])):
- if allIns[0][player][i]:
- isAllIn=True
- if (len(action_types[1][player])>0 or isAllIn):
- myStreet1Seen=True
-
- for i in range(len(allIns[1][player])):
- if allIns[1][player][i]:
- isAllIn=True
- if (len(action_types[2][player])>0 or isAllIn):
- myStreet2Seen=True
-
- for i in range(len(allIns[2][player])):
- if allIns[2][player][i]:
- isAllIn=True
- if (len(action_types[3][player])>0 or isAllIn):
- myStreet3Seen=True
-
- #print "base:", base
- if base=="hold":
- mySawShowdown=True
- for count in range (len(action_types[3][player])):
- if action_types[3][player][count]=="fold":
- mySawShowdown=False
- else:
- #print "in else"
- for i in range(len(allIns[3][player])):
- if allIns[3][player][i]:
- isAllIn=True
- if (len(action_types[4][player])>0 or isAllIn):
- #print "in if"
- myStreet4Seen=True
-
- mySawShowdown=True
- for count in range (len(action_types[4][player])):
- if action_types[4][player][count]=="fold":
- mySawShowdown=False
-
-
- #flop stuff
- street=1
- if myStreet1Seen:
- for count in range(len(action_types[street][player])):
- if action_types[street][player][count]=="bet":
- myStreet1Aggr=True
-
- for otherPlayer in range (len(player_ids)):
- if player==otherPlayer:
- pass
- else:
- for countOther in range (len(action_types[street][otherPlayer])):
- if action_types[street][otherPlayer][countOther]=="bet":
- myOtherRaisedStreet1=True
- for countOtherFold in range (len(action_types[street][player])):
- if action_types[street][player][countOtherFold]=="fold":
- myFoldToOtherRaisedStreet1=True
-
- #turn stuff - copy of flop with different vars
- street=2
- if myStreet2Seen:
- for count in range(len(action_types[street][player])):
- if action_types[street][player][count]=="bet":
- myStreet2Aggr=True
-
- for otherPlayer in range (len(player_ids)):
- if player==otherPlayer:
- pass
- else:
- for countOther in range (len(action_types[street][otherPlayer])):
- if action_types[street][otherPlayer][countOther]=="bet":
- myOtherRaisedStreet2=True
- for countOtherFold in range (len(action_types[street][player])):
- if action_types[street][player][countOtherFold]=="fold":
- myFoldToOtherRaisedStreet2=True
-
- #river stuff - copy of flop with different vars
- street=3
- if myStreet3Seen:
- for count in range(len(action_types[street][player])):
- if action_types[street][player][count]=="bet":
- myStreet3Aggr=True
-
- for otherPlayer in range (len(player_ids)):
- if player==otherPlayer:
- pass
- else:
- for countOther in range (len(action_types[street][otherPlayer])):
- if action_types[street][otherPlayer][countOther]=="bet":
- myOtherRaisedStreet3=True
- for countOtherFold in range (len(action_types[street][player])):
- if action_types[street][player][countOtherFold]=="fold":
- myFoldToOtherRaisedStreet3=True
-
- #stud river stuff - copy of flop with different vars
- street=4
- if myStreet4Seen:
- for count in range(len(action_types[street][player])):
- if action_types[street][player][count]=="bet":
- myStreet4Aggr=True
-
- for otherPlayer in range (len(player_ids)):
- if player==otherPlayer:
- pass
- else:
- for countOther in range (len(action_types[street][otherPlayer])):
- if action_types[street][otherPlayer][countOther]=="bet":
- myOtherRaisedStreet4=True
- for countOtherFold in range (len(action_types[street][player])):
- if action_types[street][player][countOtherFold]=="fold":
- myFoldToOtherRaisedStreet4=True
-
- if winnings[player]!=0:
- if myStreet1Seen:
- myWonWhenSeenStreet1=winnings[player]/float(totalWinnings)
- if mySawShowdown:
- myWonAtSD=myWonWhenSeenStreet1
-
- #add each value to the appropriate array
- street0VPI.append(myStreet0VPI)
- street0Aggr.append(myStreet0Aggr)
- street0_3B4BChance.append(myStreet0_3B4BChance)
- street0_3B4BDone.append(myStreet0_3B4BDone)
- street1Seen.append(myStreet1Seen)
- street2Seen.append(myStreet2Seen)
- street3Seen.append(myStreet3Seen)
- street4Seen.append(myStreet4Seen)
- sawShowdown.append(mySawShowdown)
- street1Aggr.append(myStreet1Aggr)
- street2Aggr.append(myStreet2Aggr)
- street3Aggr.append(myStreet3Aggr)
- street4Aggr.append(myStreet4Aggr)
- otherRaisedStreet1.append(myOtherRaisedStreet1)
- otherRaisedStreet2.append(myOtherRaisedStreet2)
- otherRaisedStreet3.append(myOtherRaisedStreet3)
- otherRaisedStreet4.append(myOtherRaisedStreet4)
- foldToOtherRaisedStreet1.append(myFoldToOtherRaisedStreet1)
- foldToOtherRaisedStreet2.append(myFoldToOtherRaisedStreet2)
- foldToOtherRaisedStreet3.append(myFoldToOtherRaisedStreet3)
- foldToOtherRaisedStreet4.append(myFoldToOtherRaisedStreet4)
- wonWhenSeenStreet1.append(myWonWhenSeenStreet1)
- wonAtSD.append(myWonAtSD)
- stealAttemptChance.append(myStealAttemptChance)
- stealAttempted.append(myStealAttempted)
- if base=="hold":
- pos=positions[player]
- if pos=='B':
- hudDataPositions.append('B')
- elif pos=='S':
- hudDataPositions.append('S')
- elif pos==0:
- hudDataPositions.append('D')
- elif pos==1:
- hudDataPositions.append('C')
- elif pos>=2 and pos<=4:
- hudDataPositions.append('M')
- elif pos>=5 and pos<=7:
- hudDataPositions.append('E')
- ### RHH Added this elif to handle being a dead hand before the BB (pos==9)
- elif pos==9:
- hudDataPositions.append('X')
- else:
- raise FpdbError("invalid position")
- elif base=="stud":
- #todo: stud positions and steals
- pass
-
- #add each array to the to-be-returned dictionary
- result={'street0VPI':street0VPI}
- result['street0Aggr']=street0Aggr
- result['street0_3B4BChance']=street0_3B4BChance
- result['street0_3B4BDone']=street0_3B4BDone
- result['street1Seen']=street1Seen
- result['street2Seen']=street2Seen
- result['street3Seen']=street3Seen
- result['street4Seen']=street4Seen
- result['sawShowdown']=sawShowdown
-
- result['street1Aggr']=street1Aggr
- result['otherRaisedStreet1']=otherRaisedStreet1
- result['foldToOtherRaisedStreet1']=foldToOtherRaisedStreet1
- result['street2Aggr']=street2Aggr
- result['otherRaisedStreet2']=otherRaisedStreet2
- result['foldToOtherRaisedStreet2']=foldToOtherRaisedStreet2
- result['street3Aggr']=street3Aggr
- result['otherRaisedStreet3']=otherRaisedStreet3
- result['foldToOtherRaisedStreet3']=foldToOtherRaisedStreet3
- result['street4Aggr']=street4Aggr
- result['otherRaisedStreet4']=otherRaisedStreet4
- result['foldToOtherRaisedStreet4']=foldToOtherRaisedStreet4
- result['wonWhenSeenStreet1']=wonWhenSeenStreet1
- result['wonAtSD']=wonAtSD
- result['stealAttemptChance']=stealAttemptChance
- result['stealAttempted']=stealAttempted
-
- #now the various steal values
- foldBbToStealChance=[]
- foldedBbToSteal=[]
- foldSbToStealChance=[]
- foldedSbToSteal=[]
- for player in range (len(player_ids)):
- myFoldBbToStealChance=False
- myFoldedBbToSteal=False
- myFoldSbToStealChance=False
- myFoldedSbToSteal=False
-
- if base=="hold":
- if someoneStole and (positions[player]=='B' or positions[player]=='S') and firstPfRaiserId!=player_ids[player]:
- street=0
- for count in range (len(action_types[street][player])):#individual actions
- if positions[player]=='B':
- myFoldBbToStealChance=True
- if action_types[street][player][count]=="fold":
- myFoldedBbToSteal=True
- if positions[player]=='S':
- myFoldSbToStealChance=True
- if action_types[street][player][count]=="fold":
- myFoldedSbToSteal=True
-
-
- foldBbToStealChance.append(myFoldBbToStealChance)
- foldedBbToSteal.append(myFoldedBbToSteal)
- foldSbToStealChance.append(myFoldSbToStealChance)
- foldedSbToSteal.append(myFoldedSbToSteal)
- result['foldBbToStealChance']=foldBbToStealChance
- result['foldedBbToSteal']=foldedBbToSteal
- result['foldSbToStealChance']=foldSbToStealChance
- result['foldedSbToSteal']=foldedSbToSteal
-
- #now CB
- street1CBChance=[]
- street1CBDone=[]
- didStreet1CB=[]
- for player in range (len(player_ids)):
- myStreet1CBChance=False
- myStreet1CBDone=False
-
- if street0VPI[player]:
- myStreet1CBChance=True
- if street1Aggr[player]:
- myStreet1CBDone=True
- didStreet1CB.append(player_ids[player])
-
- street1CBChance.append(myStreet1CBChance)
- street1CBDone.append(myStreet1CBDone)
- result['street1CBChance']=street1CBChance
- result['street1CBDone']=street1CBDone
-
- #now 2B
- street2CBChance=[]
- street2CBDone=[]
- didStreet2CB=[]
- for player in range (len(player_ids)):
- myStreet2CBChance=False
- myStreet2CBDone=False
-
- if street1CBDone[player]:
- myStreet2CBChance=True
- if street2Aggr[player]:
- myStreet2CBDone=True
- didStreet2CB.append(player_ids[player])
-
- street2CBChance.append(myStreet2CBChance)
- street2CBDone.append(myStreet2CBDone)
- result['street2CBChance']=street2CBChance
- result['street2CBDone']=street2CBDone
-
- #now 3B
- street3CBChance=[]
- street3CBDone=[]
- didStreet3CB=[]
- for player in range (len(player_ids)):
- myStreet3CBChance=False
- myStreet3CBDone=False
-
- if street2CBDone[player]:
- myStreet3CBChance=True
- if street3Aggr[player]:
- myStreet3CBDone=True
- didStreet3CB.append(player_ids[player])
-
- street3CBChance.append(myStreet3CBChance)
- street3CBDone.append(myStreet3CBDone)
- result['street3CBChance']=street3CBChance
- result['street3CBDone']=street3CBDone
-
- #and 4B
- street4CBChance=[]
- street4CBDone=[]
- didStreet4CB=[]
- for player in range (len(player_ids)):
- myStreet4CBChance=False
- myStreet4CBDone=False
-
- if street3CBDone[player]:
- myStreet4CBChance=True
- if street4Aggr[player]:
- myStreet4CBDone=True
- didStreet4CB.append(player_ids[player])
-
- street4CBChance.append(myStreet4CBChance)
- street4CBDone.append(myStreet4CBDone)
- result['street4CBChance']=street4CBChance
- result['street4CBDone']=street4CBDone
-
-
- result['position']=hudDataPositions
-
- foldToStreet1CBChance=[]
- foldToStreet1CBDone=[]
- foldToStreet2CBChance=[]
- foldToStreet2CBDone=[]
- foldToStreet3CBChance=[]
- foldToStreet3CBDone=[]
- foldToStreet4CBChance=[]
- foldToStreet4CBDone=[]
-
- for player in range (len(player_ids)):
- myFoldToStreet1CBChance=False
- myFoldToStreet1CBDone=False
- foldToStreet1CBChance.append(myFoldToStreet1CBChance)
- foldToStreet1CBDone.append(myFoldToStreet1CBDone)
-
- myFoldToStreet2CBChance=False
- myFoldToStreet2CBDone=False
- foldToStreet2CBChance.append(myFoldToStreet2CBChance)
- foldToStreet2CBDone.append(myFoldToStreet2CBDone)
-
- myFoldToStreet3CBChance=False
- myFoldToStreet3CBDone=False
- foldToStreet3CBChance.append(myFoldToStreet3CBChance)
- foldToStreet3CBDone.append(myFoldToStreet3CBDone)
-
- myFoldToStreet4CBChance=False
- myFoldToStreet4CBDone=False
- foldToStreet4CBChance.append(myFoldToStreet4CBChance)
- foldToStreet4CBDone.append(myFoldToStreet4CBDone)
-
- if len(didStreet1CB)>=1:
- generateFoldToCB(1, player_ids, didStreet1CB, street1CBDone, foldToStreet1CBChance, foldToStreet1CBDone, actionTypeByNo)
-
- if len(didStreet2CB)>=1:
- generateFoldToCB(2, player_ids, didStreet2CB, street2CBDone, foldToStreet2CBChance, foldToStreet2CBDone, actionTypeByNo)
-
- if len(didStreet3CB)>=1:
- generateFoldToCB(3, player_ids, didStreet3CB, street3CBDone, foldToStreet3CBChance, foldToStreet3CBDone, actionTypeByNo)
-
- if len(didStreet4CB)>=1:
- generateFoldToCB(4, player_ids, didStreet4CB, street4CBDone, foldToStreet4CBChance, foldToStreet4CBDone, actionTypeByNo)
-
- result['foldToStreet1CBChance']=foldToStreet1CBChance
- result['foldToStreet1CBDone']=foldToStreet1CBDone
- result['foldToStreet2CBChance']=foldToStreet2CBChance
- result['foldToStreet2CBDone']=foldToStreet2CBDone
- result['foldToStreet3CBChance']=foldToStreet3CBChance
- result['foldToStreet3CBDone']=foldToStreet3CBDone
- result['foldToStreet4CBChance']=foldToStreet4CBChance
- result['foldToStreet4CBDone']=foldToStreet4CBDone
-
-
- totalProfit=[]
-
- street1CheckCallRaiseChance=[]
- street1CheckCallRaiseDone=[]
- street2CheckCallRaiseChance=[]
- street2CheckCallRaiseDone=[]
- street3CheckCallRaiseChance=[]
- street3CheckCallRaiseDone=[]
- street4CheckCallRaiseChance=[]
- street4CheckCallRaiseDone=[]
- #print "b4 totprof calc, len(playerIds)=", len(player_ids)
- for pl in range (len(player_ids)):
- #print "pl=", pl
- myTotalProfit=winnings[pl] # still need to deduct costs
- for i in range (len(actionTypes)): #iterate through streets
- #for j in range (len(actionTypes[i])): #iterate through names (using pl loop above)
- for k in range (len(actionTypes[i][pl])): #iterate through individual actions of that player on that street
- myTotalProfit -= actionAmounts[i][pl][k]
-
- myStreet1CheckCallRaiseChance=False
- myStreet1CheckCallRaiseDone=False
- myStreet2CheckCallRaiseChance=False
- myStreet2CheckCallRaiseDone=False
- myStreet3CheckCallRaiseChance=False
- myStreet3CheckCallRaiseDone=False
- myStreet4CheckCallRaiseChance=False
- myStreet4CheckCallRaiseDone=False
-
- #print "myTotalProfit=", myTotalProfit
- totalProfit.append(myTotalProfit)
- #print "totalProfit[]=", totalProfit
-
- street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance)
- street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone)
- street2CheckCallRaiseChance.append(myStreet2CheckCallRaiseChance)
- street2CheckCallRaiseDone.append(myStreet2CheckCallRaiseDone)
- street3CheckCallRaiseChance.append(myStreet3CheckCallRaiseChance)
- street3CheckCallRaiseDone.append(myStreet3CheckCallRaiseDone)
- street4CheckCallRaiseChance.append(myStreet4CheckCallRaiseChance)
- street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone)
-
- result['totalProfit']=totalProfit
- #print "res[totalProfit]=", result['totalProfit']
-
- result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance
- result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone
- result['street2CheckCallRaiseChance']=street2CheckCallRaiseChance
- result['street2CheckCallRaiseDone']=street2CheckCallRaiseDone
- result['street3CheckCallRaiseChance']=street3CheckCallRaiseChance
- result['street3CheckCallRaiseDone']=street3CheckCallRaiseDone
- result['street4CheckCallRaiseChance']=street4CheckCallRaiseChance
- result['street4CheckCallRaiseDone']=street4CheckCallRaiseDone
- return result
-#end def generateHudCacheData
-
-def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetCBChance, foldToStreetCBDone, actionTypeByNo):
- """fills the passed foldToStreetCB* arrays appropriately depending on the given street"""
- #print "beginning of generateFoldToCB, street:", street, "len(actionTypeByNo):", len(actionTypeByNo)
- #print "len(actionTypeByNo[street]):",len(actionTypeByNo[street])
- firstCBReaction=0
- for action in range(len(actionTypeByNo[street])):
- if actionTypeByNo[street][action][1]=="bet":
- for player in didStreetCB:
- if player==actionTypeByNo[street][action][0] and firstCBReaction==0:
- firstCBReaction=action+1
- break
-
- for action in actionTypeByNo[street][firstCBReaction:]:
- for player in range(len(playerIDs)):
- if playerIDs[player]==action[0]:
- foldToStreetCBChance[player]=True
- if action[1]=="fold":
- foldToStreetCBDone[player]=True
-#end def generateFoldToCB
-
-def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
-# if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
-
- #print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \
- #, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
- for player in range (len(playerIds)):
- if base=="hold":
- cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player]))
- else:
- cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s", (gametypeId, playerIds[player], len(playerIds)))
- row=cursor.fetchone()
- #print "gametypeId:", gametypeId, "playerIds[player]",playerIds[player], "len(playerIds):",len(playerIds), "row:",row
-
- try: len(row)
- except TypeError:
- row=[]
-
- if (len(row)==0):
- #print "new huddata row"
- doInsert=True
- row=[]
- row.append(0)#blank for id
- row.append(gametypeId)
- row.append(playerIds[player])
- row.append(len(playerIds))#seats
- for i in range(len(hudImportData)+2):
- row.append(0)
-
- else:
- doInsert=False
- newrow=[]
- for i in range(len(row)):
- newrow.append(row[i])
- row=newrow
-
- if base=="hold":
- row[4]=hudImportData['position'][player]
- else:
- row[4]=0
- row[5]=1 #tourneysGametypeId
- row[6]+=1 #HDs
- if hudImportData['street0VPI'][player]: row[7]+=1
- if hudImportData['street0Aggr'][player]: row[8]+=1
- if hudImportData['street0_3B4BChance'][player]: row[9]+=1
- if hudImportData['street0_3B4BDone'][player]: row[10]+=1
- if hudImportData['street1Seen'][player]: row[11]+=1
- if hudImportData['street2Seen'][player]: row[12]+=1
- if hudImportData['street3Seen'][player]: row[13]+=1
- if hudImportData['street4Seen'][player]: row[14]+=1
- if hudImportData['sawShowdown'][player]: row[15]+=1
- if hudImportData['street1Aggr'][player]: row[16]+=1
- if hudImportData['street2Aggr'][player]: row[17]+=1
- if hudImportData['street3Aggr'][player]: row[18]+=1
- if hudImportData['street4Aggr'][player]: row[19]+=1
- if hudImportData['otherRaisedStreet1'][player]: row[20]+=1
- if hudImportData['otherRaisedStreet2'][player]: row[21]+=1
- if hudImportData['otherRaisedStreet3'][player]: row[22]+=1
- if hudImportData['otherRaisedStreet4'][player]: row[23]+=1
- if hudImportData['foldToOtherRaisedStreet1'][player]: row[24]+=1
- if hudImportData['foldToOtherRaisedStreet2'][player]: row[25]+=1
- if hudImportData['foldToOtherRaisedStreet3'][player]: row[26]+=1
- if hudImportData['foldToOtherRaisedStreet4'][player]: row[27]+=1
- if hudImportData['wonWhenSeenStreet1'][player]!=0.0: row[28]+=hudImportData['wonWhenSeenStreet1'][player]
- if hudImportData['wonAtSD'][player]!=0.0: row[29]+=hudImportData['wonAtSD'][player]
- if hudImportData['stealAttemptChance'][player]: row[30]+=1
- if hudImportData['stealAttempted'][player]: row[31]+=1
- if hudImportData['foldBbToStealChance'][player]: row[32]+=1
- if hudImportData['foldedBbToSteal'][player]: row[33]+=1
- if hudImportData['foldSbToStealChance'][player]: row[34]+=1
- if hudImportData['foldedSbToSteal'][player]: row[35]+=1
-
- if hudImportData['street1CBChance'][player]: row[36]+=1
- if hudImportData['street1CBDone'][player]: row[37]+=1
- if hudImportData['street2CBChance'][player]: row[38]+=1
- if hudImportData['street2CBDone'][player]: row[39]+=1
- if hudImportData['street3CBChance'][player]: row[40]+=1
- if hudImportData['street3CBDone'][player]: row[41]+=1
- if hudImportData['street4CBChance'][player]: row[42]+=1
- if hudImportData['street4CBDone'][player]: row[43]+=1
-
- if hudImportData['foldToStreet1CBChance'][player]: row[44]+=1
- if hudImportData['foldToStreet1CBDone'][player]: row[45]+=1
- if hudImportData['foldToStreet2CBChance'][player]: row[46]+=1
- if hudImportData['foldToStreet2CBDone'][player]: row[47]+=1
- if hudImportData['foldToStreet3CBChance'][player]: row[48]+=1
- if hudImportData['foldToStreet3CBDone'][player]: row[49]+=1
- if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1
- if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1
-
- #print "player=", player
- #print "len(totalProfit)=", len(hudImportData['totalProfit'])
- if hudImportData['totalProfit'][player]:
- row[52]+=hudImportData['totalProfit'][player]
-
- if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1
- if hudImportData['street1CheckCallRaiseDone'][player]: row[54]+=1
- if hudImportData['street2CheckCallRaiseChance'][player]: row[55]+=1
- if hudImportData['street2CheckCallRaiseDone'][player]: row[56]+=1
- if hudImportData['street3CheckCallRaiseChance'][player]: row[57]+=1
- if hudImportData['street3CheckCallRaiseDone'][player]: row[58]+=1
- if hudImportData['street4CheckCallRaiseChance'][player]: row[59]+=1
- if hudImportData['street4CheckCallRaiseDone'][player]: row[60]+=1
-
- if doInsert:
- #print "playerid before insert:",row[2]
- cursor.execute("""INSERT INTO HudCache
- (gametypeId, playerId, activeSeats, position, tourneyTypeId,
- HDs, street0VPI, street0Aggr, street0_3B4BChance, street0_3B4BDone,
- street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
- street1Aggr, street2Aggr, street3Aggr, street4Aggr, otherRaisedStreet1,
- otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4, foldToOtherRaisedStreet1, foldToOtherRaisedStreet2,
- foldToOtherRaisedStreet3, foldToOtherRaisedStreet4, wonWhenSeenStreet1, wonAtSD, stealAttemptChance,
- stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal,
- street1CBChance, street1CBDone, street2CBChance, street2CBDone, street3CBChance,
- street3CBDone, street4CBChance, street4CBDone, foldToStreet1CBChance, foldToStreet1CBDone,
- foldToStreet2CBChance, foldToStreet2CBDone, foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance,
- foldToStreet4CBDone, totalProfit, street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance,
- street2CheckCallRaiseDone, street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone)
- VALUES (%s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s,
- %s, %s, %s, %s, %s)""", (row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40], row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50], row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60]))
- else:
- #print "storing updated hud data line"
- cursor.execute("""UPDATE HudCache
- SET HDs=%s, street0VPI=%s, street0Aggr=%s, street0_3B4BChance=%s, street0_3B4BDone=%s,
- street1Seen=%s, street2Seen=%s, street3Seen=%s, street4Seen=%s, sawShowdown=%s,
- street1Aggr=%s, street2Aggr=%s, street3Aggr=%s, street4Aggr=%s, otherRaisedStreet1=%s,
- otherRaisedStreet2=%s, otherRaisedStreet3=%s, otherRaisedStreet4=%s, foldToOtherRaisedStreet1=%s, foldToOtherRaisedStreet2=%s,
- foldToOtherRaisedStreet3=%s, foldToOtherRaisedStreet4=%s, wonWhenSeenStreet1=%s, wonAtSD=%s, stealAttemptChance=%s,
- stealAttempted=%s, foldBbToStealChance=%s, foldedBbToSteal=%s, foldSbToStealChance=%s, foldedSbToSteal=%s,
- street1CBChance=%s, street1CBDone=%s, street2CBChance=%s, street2CBDone=%s, street3CBChance=%s,
- street3CBDone=%s, street4CBChance=%s, street4CBDone=%s, foldToStreet1CBChance=%s, foldToStreet1CBDone=%s,
- foldToStreet2CBChance=%s, foldToStreet2CBDone=%s, foldToStreet3CBChance=%s, foldToStreet3CBDone=%s, foldToStreet4CBChance=%s,
- foldToStreet4CBDone=%s, totalProfit=%s, street1CheckCallRaiseChance=%s, street1CheckCallRaiseDone=%s, street2CheckCallRaiseChance=%s,
- street2CheckCallRaiseDone=%s, street3CheckCallRaiseChance=%s, street3CheckCallRaiseDone=%s, street4CheckCallRaiseChance=%s, street4CheckCallRaiseDone=%s
- WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s AND position=%s AND tourneyTypeId=%s""", (row[6], row[7], row[8], row[9], row[10],
- row[11], row[12], row[13], row[14], row[15],
- row[16], row[17], row[18], row[19], row[20],
- row[21], row[22], row[23], row[24], row[25],
- row[26], row[27], row[28], row[29], row[30],
- row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40],
- row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50],
- row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60],
- row[1], row[2], row[3], str(row[4]), row[5]))
-# else:
-# print "todo: implement storeHudCache for stud base"
-#end def storeHudCache
-
-def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime):
- cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId))
- tmp=cursor.fetchone()
- #print "tried SELECTing tourneys.id, result:",tmp
-
- try:
- len(tmp)
- except TypeError:#means we have to create new one
- cursor.execute("""INSERT INTO Tourneys
- (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)
- VALUES (%s, %s, %s, %s, %s)""", (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime))
- cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId))
- tmp=cursor.fetchone()
- #print "created new tourneys.id:",tmp
- return tmp[0]
-#end def store_tourneys
-
-def store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings):
- result=[]
- #print "in store_tourneys_players. tourney_id:",tourney_id
- #print "player_ids:",player_ids
- #print "payin_amounts:",payin_amounts
- #print "ranks:",ranks
- #print "winnings:",winnings
- for i in range (len(player_ids)):
- cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", (tourney_id, player_ids[i]))
- tmp=cursor.fetchone()
- #print "tried SELECTing tourneys_players.id:",tmp
-
- try:
- len(tmp)
- except TypeError:
- cursor.execute("""INSERT INTO TourneysPlayers
- (tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""",
- (tourney_id, player_ids[i], payin_amounts[i], ranks[i], winnings[i]))
-
- cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s",
- (tourney_id, player_ids[i]))
- tmp=cursor.fetchone()
- #print "created new tourneys_players.id:",tmp
- result.append(tmp[0])
- return result
-#end def store_tourneys_players
+#!/usr/bin/python
+
+#Copyright 2008 Steffen Jobbagy-Felso
+#This program is free software: you can redistribute it and/or modify
+#it under the terms of the GNU Affero General Public License as published by
+#the Free Software Foundation, version 3 of the License.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU Affero General Public License
+#along with this program. If not, see .
+#In the "official" distribution you can find the license in
+#agpl-3.0.txt in the docs folder of the package.
+
+#This file contains simple functions for fpdb
+
+import datetime
+import re
+
+PS=1
+FTP=2
+
+class DuplicateError(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+class FpdbError(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+#returns an array of the total money paid. intending to add rebuys/addons here
+def calcPayin(count, buyin, fee):
+ result=[]
+ for i in range(count):
+ result.append (buyin+fee)
+ return result
+#end def calcPayin
+
+def checkPositions(positions):
+ """verifies that these positions are valid"""
+ for i in range (len(positions)):
+ pos=positions[i]
+ try:#todo: use type recognition instead of error
+ if (len(pos)!=1):
+ raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+pos) #dont need to str() here
+ except TypeError:#->not string->is int->fine
+ pass
+
+ ### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB
+ ### eric - position 8 could be valid - if only one blind is posted, but there's still 10 people, ie a sitout is present, and the small is dead...
+ if not (pos == "B" or pos == "S" or (pos >= 0 and pos <= 9)):
+ raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos))
+#end def fpdb_simple.checkPositions
+
+#classifies each line for further processing in later code. Manipulates the passed arrays.
+def classifyLines(hand, category, lineTypes, lineStreets):
+ currentStreet="predeal"
+ done=False #set this to true once we reach the last relevant line (the summary, except rake, is all repeats)
+ for i in range (len(hand)):
+ if (done):
+ if (hand[i].find("[")==-1 or hand[i].find("mucked [")==-1):
+ lineTypes.append("ignore")
+ else: #it's storing a mucked card
+ lineTypes.append("cards")
+ elif (hand[i].startswith("Dealt to")):
+ lineTypes.append("cards")
+ elif (i==0):
+ lineTypes.append("header")
+ elif (hand[i].startswith("Seat ") and ((hand[i].find("in chips")!=-1) or (hand[i].find("($")!=-1))):
+ lineTypes.append("name")
+ elif (isActionLine(hand[i])):
+ lineTypes.append("action")
+ if (hand[i].find(" posts ")!=-1 or hand[i].find(" posts the ")!=-1):#need to set this here so the "action" of posting blinds is registered properly
+ currentStreet="preflop"
+ elif (isWinLine(hand[i])):
+ lineTypes.append("win")
+ elif (hand[i].startswith("Total pot ") and hand[i].find("Rake")!=-1):
+ lineTypes.append("rake")
+ done=True
+ elif (hand[i]=="*** SHOW DOWN ***" or hand[i]=="*** SUMMARY ***"):
+ lineTypes.append("ignore")
+ #print "in classifyLine, showdown or summary"
+ elif (hand[i].find(" antes ")!=-1 or hand[i].find(" posts the ante ")!=-1):
+ lineTypes.append("ante")
+ elif (hand[i].startswith("*** FLOP *** [")):
+ lineTypes.append("cards")
+ currentStreet="flop"
+ elif (hand[i].startswith("*** TURN *** [")):
+ lineTypes.append("cards")
+ currentStreet="turn"
+ elif (hand[i].startswith("*** RIVER *** [")):
+ lineTypes.append("cards")
+ currentStreet="river"
+ elif (hand[i].startswith("*** 3")):
+ lineTypes.append("ignore")
+ currentStreet=0
+ elif (hand[i].startswith("*** 4")):
+ lineTypes.append("ignore")
+ currentStreet=1
+ elif (hand[i].startswith("*** 5")):
+ lineTypes.append("ignore")
+ currentStreet=2
+ elif (hand[i].startswith("*** 6")):
+ lineTypes.append("ignore")
+ currentStreet=3
+ elif (hand[i].startswith("*** 7") or hand[i]=="*** RIVER ***"):
+ lineTypes.append("ignore")
+ currentStreet=4
+ elif (hand[i].find(" shows [")!=-1):
+ lineTypes.append("cards")
+ elif (hand[i].startswith("Table '")):
+ lineTypes.append("table")
+ else:
+ raise FpdbError("unrecognised linetype in:"+hand[i])
+ lineStreets.append(currentStreet)
+#end def classifyLines
+
+def convert3B4B(site, category, limit_type, actionTypes, actionAmounts):
+ """calculates the actual bet amounts in the given amount array and changes it accordingly."""
+ for i in range (len(actionTypes)):
+ for j in range (len(actionTypes[i])):
+ bets=[]
+ for k in range (len(actionTypes[i][j])):
+ if (actionTypes[i][j][k]=="bet"):
+ bets.append((i,j,k))
+ if (len(bets)==2):
+ #print "len(bets) 2 or higher, need to correct it. bets:",bets,"len:",len(bets)
+ amount2=actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]]
+ amount1=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]
+ actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]]=amount2-amount1
+ elif (len(bets)>2):
+ fail=True
+ #todo: run correction for below
+ if (site=="ps" and category=="holdem" and limit_type=="nl" and len(bets)==3):
+ fail=False
+ if (site=="ftp" and category=="omahahi" and limit_type=="pl" and len(bets)==3):
+ fail=False
+
+ if fail:
+ print "len(bets)>2 in convert3B4B, i didnt think this is possible. i:",i,"j:",j,"k:",k
+ print "actionTypes:",actionTypes
+ raise FpdbError ("too many bets in convert3B4B")
+ #print "actionAmounts postConvert",actionAmounts
+#end def convert3B4B(actionTypes, actionAmounts)
+
+#Corrects the bet amount if the player had to pay blinds
+def convertBlindBet(actionTypes, actionAmounts):
+ i=0#setting street to pre-flop
+ for j in range (len(actionTypes[i])):#playerloop
+ blinds=[]
+ bets=[]
+ for k in range (len(actionTypes[i][j])):
+ if (actionTypes[i][j][k]=="blind"):
+ blinds.append((i,j,k))
+
+ if (len(blinds)>0 and actionTypes[i][j][k]=="bet"):
+ bets.append((i,j,k))
+ if (len(bets)==1):
+ blind_amount=actionAmounts[blinds[0][0]][blinds[0][1]][blinds[0][2]]
+ bet_amount=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]
+ actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]=bet_amount-blind_amount
+#end def convertBlindBet
+
+#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details
+#todo: make this use convertCardValuesBoard
+def convertCardValues(arr):
+ for i in range (len(arr)):
+ for j in range (len(arr[i])):
+ if (arr[i][j]=="A"):
+ arr[i][j]=14
+ elif (arr[i][j]=="K"):
+ arr[i][j]=13
+ elif (arr[i][j]=="Q"):
+ arr[i][j]=12
+ elif (arr[i][j]=="J"):
+ arr[i][j]=11
+ elif (arr[i][j]=="T"):
+ arr[i][j]=10
+ else:
+ arr[i][j]=int(arr[i][j])
+#end def convertCardValues
+
+#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details
+def convertCardValuesBoard(arr):
+ for i in range (len(arr)):
+ if (arr[i]=="A"):
+ arr[i]=14
+ elif (arr[i]=="K"):
+ arr[i]=13
+ elif (arr[i]=="Q"):
+ arr[i]=12
+ elif (arr[i]=="J"):
+ arr[i]=11
+ elif (arr[i]=="T"):
+ arr[i]=10
+ else:
+ arr[i]=int(arr[i])
+#end def convertCardValuesBoard
+
+#this creates the 2D/3D arrays. manipulates the passed arrays instead of returning.
+def createArrays(category, seats, card_values, card_suits, antes, winnings, rakes, action_types, allIns, action_amounts, actionNos, actionTypeByNo):
+ for i in range(seats):#create second dimension arrays
+ tmp=[]
+ card_values.append(tmp)
+ tmp=[]
+ card_suits.append(tmp)
+ antes.append(0)
+ winnings.append(0)
+ rakes.append(0)
+
+ if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
+ streetCount=4
+ else:
+ streetCount=5
+
+ for i in range(streetCount): #build the first dimension array, for streets
+ tmp=[]
+ action_types.append(tmp)
+ tmp=[]
+ allIns.append(tmp)
+ tmp=[]
+ action_amounts.append(tmp)
+ tmp=[]
+ actionNos.append(tmp)
+ tmp=[]
+ actionTypeByNo.append(tmp)
+ for j in range (seats): #second dimension arrays: players
+ tmp=[]
+ action_types[i].append(tmp)
+ tmp=[]
+ allIns[i].append(tmp)
+ tmp=[]
+ action_amounts[i].append(tmp)
+ tmp=[]
+ actionNos[i].append(tmp)
+ if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
+ pass
+ elif (category=="razz" or category=="studhi" or category=="studhilo"):#need to fill card arrays.
+ for i in range(seats):
+ for j in range (7):
+ card_values[i].append(0)
+ card_suits[i].append("x")
+ else:
+ raise FpdbError("invalid category")
+#end def createArrays
+
+def fill_board_cards(board_values, board_suits):
+#fill up the two board card arrays
+ while (len(board_values)<5):
+ board_values.append(0)
+ board_suits.append("x")
+#end def fill_board_cards
+
+def fillCardArrays(player_count, base, category, card_values, card_suits):
+ """fills up the two card arrays"""
+ if (category=="holdem"):
+ cardCount=2
+ elif (category=="omahahi" or category=="omahahilo"):
+ cardCount=4
+ elif base=="stud":
+ cardCount=7
+ else:
+ raise fpdb_simple.FpdbError ("invalid category:", category)
+
+ for i in range (player_count):
+ while (len(card_values[i])=1):
+ raise DuplicateError ("dupl")
+#end isAlreadyInDB
+
+def isRebuyOrAddon(topline):
+ """isRebuyOrAddon not implemented yet"""
+ return False
+#end def isRebuyOrAddon
+
+#returns whether the passed topline indicates a tournament or not
+def isTourney(topline):
+ if (topline.find("Tournament")!=-1):
+ return True
+ else:
+ return False
+#end def isTourney
+
+#returns boolean whether the passed line is a win line
+def isWinLine(line):
+ if (line.find("wins the pot")!=-1):
+ return True
+ elif (line.find("ties for the high pot")!=-1):
+ return True
+ elif (line.find("ties for the high main pot")!=-1):
+ return True
+ elif (line.find("ties for the high side pot")!=-1):
+ return True
+ elif (line.find("ties for the low pot")!=-1):
+ return True
+ elif (line.find("ties for the low main pot")!=-1):
+ return True
+ elif (line.find("ties for the low side pot")!=-1):
+ return True
+ elif (line.find("ties for the main pot")!=-1): #for ftp tied main pot of split pot
+ return True
+ elif (line.find("ties for the pot")!=-1): #for ftp tie
+ return True
+ elif (line.find("ties for the side pot")!=-1): #for ftp tied split pots
+ return True
+ elif (line.find("wins side pot #")!=-1): #for ftp multi split pots
+ return True
+ elif (line.find("wins the low main pot")!=-1):
+ return True
+ elif (line.find("wins the low pot")!=-1):
+ return True
+ elif (line.find("wins the low side pot")!=-1):
+ return True
+ elif (line.find("wins the high main pot")!=-1):
+ return True
+ elif (line.find("wins the high pot")!=-1):
+ return True
+ elif (line.find("wins the high side pot")!=-1):
+ return True
+ elif (line.find("wins the main pot")!=-1):
+ return True
+ elif (line.find("wins the side pot")!=-1): #for ftp split pots
+ return True
+ elif (line.find("collected")!=-1):
+ return True
+ else:
+ return False #not raising error here, any unknown line wouldve been detected in isActionLine already
+#end def isWinLine
+
+#returns the amount of cash/chips put into the put in the given action line
+def parseActionAmount(line, atype, site, isTourney):
+ #if (line.endswith(" and is all-in")):
+ # line=line[:-14]
+ #elif (line.endswith(", and is all in")):
+ # line=line[:-15]
+
+ if line.endswith(", and is capped"):#ideally we should recognise this as an all-in if category is capXl
+ line=line[:-15]
+ if line.endswith(" and is capped"):
+ line=line[:-14]
+
+
+ if (atype=="fold"):
+ amount=0
+ elif (atype=="check"):
+ amount=0
+ elif (atype=="unbet" and site=="ftp"):
+ pos1=line.find("$")+1
+ pos2=line.find(" returned to")
+ amount=float2int(line[pos1:pos2])
+ elif (atype=="unbet" and site=="ps"):
+ #print "ps unbet, line:",line
+ pos1=line.find("$")+1
+ if pos1==0:
+ pos1=line.find("(")+1
+ pos2=line.find(")")
+ amount=float2int(line[pos1:pos2])
+ elif (atype=="bet" and site=="ps" and line.find(": raises $")!=-1 and line.find("to $")!=-1):
+ pos=line.find("to $")+4
+ amount=float2int(line[pos:])
+ else:
+ if not isTourney:
+ pos=line.rfind("$")+1
+ #print "parseActionAmount, line:", line, "line[pos:]:", line[pos:]
+ amount=float2int(line[pos:])
+ else:
+ #print "line:"+line+"EOL"
+ pos=line.rfind(" ")+1
+ #print "pos:",pos
+ #print "pos of 20:", line.find("20")
+ amount=int(line[pos:])
+
+ if atype=="unbet":
+ amount*=-1
+ return amount
+#end def parseActionAmount
+
+#doesnt return anything, simply changes the passed arrays action_types and
+# action_amounts. For stud this expects numeric streets (3-7), for
+# holdem/omaha it expects predeal, preflop, flop, turn or river
+def parseActionLine(site, base, isTourney, line, street, playerIDs, names, action_types, allIns, action_amounts, actionNos, actionTypeByNo):
+ if (street=="predeal" or street=="preflop"):
+ street=0
+ elif (street=="flop"):
+ street=1
+ elif (street=="turn"):
+ street=2
+ elif (street=="river"):
+ street=3
+
+ nextActionNo=0
+ for player in range(len(actionNos[street])):
+ for count in range(len(actionNos[street][player])):
+ if actionNos[street][player][count]>=nextActionNo:
+ nextActionNo=actionNos[street][player][count]+1
+
+ line, allIn=goesAllInOnThisLine(line)
+ atype=parseActionType(line)
+ playerno=recognisePlayerNo(line, names, atype)
+ amount=parseActionAmount(line, atype, site, isTourney)
+
+ action_types[street][playerno].append(atype)
+ allIns[street][playerno].append(allIn)
+ action_amounts[street][playerno].append(amount)
+ actionNos[street][playerno].append(nextActionNo)
+ tmp=(playerIDs[playerno], atype)
+ actionTypeByNo[street].append(tmp)
+#end def parseActionLine
+
+def goesAllInOnThisLine(line):
+ """returns whether the player went all-in on this line and removes the all-in text from the line."""
+ isAllIn=False
+ if (line.endswith(" and is all-in")):
+ line=line[:-14]
+ isAllIn=True
+ elif (line.endswith(", and is all in")):
+ line=line[:-15]
+ isAllIn=True
+ return (line, isAllIn)
+#end def goesAllInOnThisLine
+
+#returns the action type code (see table design) of the given action line
+def parseActionType(line):
+ if (line.startswith("Uncalled bet")):
+ return "unbet"
+ elif (line.endswith("folds")):
+ return "fold"
+ elif (line.endswith("checks")):
+ return "check"
+ elif (line.find("calls")!=-1):
+ return "call"
+ elif (line.find("brings in for")!=-1):
+ return "blind"
+ elif (line.find("completes it to")!=-1):
+ return "bet"
+ #todo: what if someone completes instead of bringing in?
+ elif (line.find(" posts $")!=-1):
+ return "blind"
+ elif (line.find(" posts a dead ")!=-1):
+ return "blind"
+ elif (line.find(": posts small blind ")!=-1):
+ return "blind"
+ elif (line.find(" posts the small blind of $")!=-1):
+ return "blind"
+ elif (line.find(": posts big blind ")!=-1):
+ return "blind"
+ elif (line.find(" posts the big blind of $")!=-1):
+ return "blind"
+ elif (line.find(": posts small & big blinds $")!=-1):
+ return "blind"
+ #todo: seperately record voluntary blind payments made to join table out of turn
+ elif (line.find("bets")!=-1):
+ return "bet"
+ elif (line.find("raises")!=-1):
+ return "bet"
+ else:
+ raise FpdbError ("failed to recognise actiontype in parseActionLine in: "+line)
+#end def parseActionType
+
+#parses the ante out of the given line and checks which player paid it, updates antes accordingly.
+def parseAnteLine(line, site, isTourney, names, antes):
+ for i in range(len(names)):
+ if (line.startswith(names[i].encode("latin-1"))): #found the ante'er
+ pos=line.rfind("$")+1
+ if not isTourney:
+ antes[i]+=float2int(line[pos:])
+ else:
+ if line.find("all-in")==-1:
+ pos=line.rfind(" ")+1
+ antes[i]+=int(line[pos:])
+ else:
+ pos1=line.rfind("ante")+5
+ pos2=line.find(" ",pos1)
+ antes[i]+=int(line[pos1:pos2])
+ #print "parseAnteLine line: ", line, "antes[i]", antes[i], "antes", antes
+#end def parseAntes
+
+#returns the buyin of a tourney in cents
+def parseBuyin(topline):
+ pos1=topline.find("$")+1
+ pos2=topline.find("+")
+ return float2int(topline[pos1:pos2])
+#end def parseBuyin
+
+#parses a card line and changes the passed arrays accordingly
+#todo: reorganise this messy method
+def parseCardLine(site, category, street, line, names, cardValues, cardSuits, boardValues, boardSuits):
+ if (line.startswith("Dealt to ") or line.find(" shows [")!=-1 or line.find("mucked [")!=-1):
+ playerNo=recognisePlayerNo(line, names, "card") #anything but unbet will be ok for that string
+
+ pos=line.rfind("[")+1
+ if (category=="holdem"):
+ for i in (pos, pos+3):
+ cardValues[playerNo].append(line[i:i+1])
+ cardSuits[playerNo].append(line[i+1:i+2])
+ if (len(cardValues[playerNo])!=2):
+ if cardValues[playerNo][0]==cardValues[playerNo][2] and cardSuits[playerNo][1]==cardSuits[playerNo][3]: #two tests will do
+ cardValues[playerNo]=cardValues[playerNo][0:2]
+ cardSuits[playerNo]=cardSuits[playerNo][0:2]
+ else:
+ print "line:",line,"cardValues[playerNo]:",cardValues[playerNo]
+ raise FpdbError("read too many/too few holecards in parseCardLine")
+ elif (category=="omahahi" or category=="omahahilo"):
+ for i in (pos, pos+3, pos+6, pos+9):
+ cardValues[playerNo].append(line[i:i+1])
+ cardSuits[playerNo].append(line[i+1:i+2])
+ if (len(cardValues[playerNo])!=4):
+ if cardValues[playerNo][0]==cardValues[playerNo][4] and cardSuits[playerNo][3]==cardSuits[playerNo][7]: #two tests will do
+ cardValues[playerNo]=cardValues[playerNo][0:4]
+ cardSuits[playerNo]=cardSuits[playerNo][0:4]
+ else:
+ print "line:",line,"cardValues[playerNo]:",cardValues[playerNo]
+ raise FpdbError("read too many/too few holecards in parseCardLine")
+ elif (category=="razz" or category=="studhi" or category=="studhilo"):
+ if (line.find("shows")==-1 and line.find("mucked") == -1):
+ #print "parseCardLine(in stud if), street:", street
+ if line[pos+2]=="]": #-> not (hero and 3rd street)
+ cardValues[playerNo][street+2]=line[pos:pos+1]
+ cardSuits[playerNo][street+2]=line[pos+1:pos+2]
+ else:
+ #print "hero card1:", line[pos:pos+2], "hero card2:", line[pos+3:pos+5], "hero card3:", line[pos+6:pos+8],
+ cardValues[playerNo][street]=line[pos:pos+1]
+ cardSuits[playerNo][street]=line[pos+1:pos+2]
+ cardValues[playerNo][street+1]=line[pos+3:pos+4]
+ cardSuits[playerNo][street+1]=line[pos+4:pos+5]
+ cardValues[playerNo][street+2]=line[pos+6:pos+7]
+ cardSuits[playerNo][street+2]=line[pos+7:pos+8]
+ else:
+ #print "parseCardLine(in stud else), street:", street
+ cardValues[playerNo][0]=line[pos:pos+1]
+ cardSuits[playerNo][0]=line[pos+1:pos+2]
+ pos+=3
+ cardValues[playerNo][1]=line[pos:pos+1]
+ cardSuits[playerNo][1]=line[pos+1:pos+2]
+ if street==4:
+ pos=pos=line.rfind("]")-2
+ cardValues[playerNo][6]=line[pos:pos+1]
+ cardSuits[playerNo][6]=line[pos+1:pos+2]
+ #print "cardValues:", cardValues
+ #print "cardSuits:", cardSuits
+ else:
+ print "line:",line,"street:",street
+ raise FpdbError("invalid category")
+ #print "end of parseCardLine/playercards, cardValues:",cardValues
+ elif (line.startswith("*** FLOP ***")):
+ pos=line.find("[")+1
+ for i in (pos, pos+3, pos+6):
+ boardValues.append(line[i:i+1])
+ boardSuits.append(line[i+1:i+2])
+ #print boardValues
+ elif (line.startswith("*** TURN ***") or line.startswith("*** RIVER ***")):
+ pos=line.find("[")+1
+ pos=line.find("[", pos+1)+1
+ boardValues.append(line[pos:pos+1])
+ boardSuits.append(line[pos+1:pos+2])
+ #print boardValues
+ else:
+ raise FpdbError ("unrecognised line:"+line)
+#end def parseCardLine
+
+def parseCashesAndSeatNos(lines, site):
+ """parses the startCashes and seatNos of each player out of the given lines and returns them as a dictionary of two arrays"""
+ cashes = []
+ seatNos = []
+ for i in range (len(lines)):
+ pos2=lines[i].find(":")
+ seatNos.append(int(lines[i][5:pos2]))
+
+ pos1=lines[i].rfind("($")+2
+ if pos1==1: #for tourneys - it's 1 instead of -1 due to adding 2 above
+ pos1=lines[i].rfind("(")+1
+ if (site=="ftp"):
+ pos2=lines[i].rfind(")")
+ elif (site=="ps"):
+ pos2=lines[i].find(" in chips")
+ cashes.append(float2int(lines[i][pos1:pos2]))
+ return {'startCashes':cashes, 'seatNos':seatNos}
+#end def parseCashesAndSeatNos
+
+#returns the buyin of a tourney in cents
+def parseFee(topline):
+ pos1=topline.find("$")+1
+ pos1=topline.find("$",pos1)+1
+ pos2=topline.find(" ", pos1)
+ return float2int(topline[pos1:pos2])
+#end def parsefee
+
+#returns a datetime object with the starttime indicated in the given topline
+def parseHandStartTime(topline, site):
+ #convert x:13:35 to 0x:13:35
+ counter=0
+ while (True):
+ pos=topline.find(" "+str(counter)+":")
+ if (pos!=-1):
+ topline=topline[0:pos+1]+"0"+topline[pos+1:]
+ counter+=1
+ if counter==10: break
+
+ isUTC=False
+ if site=="ftp":
+ pos = topline.find(" ", len(topline)-26)+1
+ tmp = topline[pos:]
+ #print "year:", tmp[14:18], "month", tmp[19:21], "day", tmp[22:24], "hour", tmp[0:2], "minute", tmp[3:5], "second", tmp[6:8]
+ result = datetime.datetime(int(tmp[14:18]), int(tmp[19:21]), int(tmp[22:24]), int(tmp[0:2]), int(tmp[3:5]), int(tmp[6:8]))
+ elif site=="ps":
+ if topline.find("UTC")!=-1:
+ pos1 = topline.find("-")+2
+ pos2 = topline.find("UTC")
+ tmp=topline[pos1:pos2]
+ isUTC=True
+ else:
+ tmp=topline
+ # print "parsehandStartTime, tmp:", tmp
+ pos = tmp.find("-")+2
+ tmp = tmp[pos:]
+ #Need to match either
+ # 2008/09/07 06:23:14 ET or
+ # 2008/08/17 - 01:14:43 (ET) or
+ # 2008/11/12 9:33:31 CET [2008/11/12 3:33:31 ET]
+ rexx = '(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P [0-9]+):(?P[0-9]+):(?P[0-9]+)'
+ m = re.search(rexx,tmp)
+ #print "year:", int(m.group('YEAR')), "month", int(m.group('MON')), "day", int(m.group('DAY')), "hour", int(m.group('HR')), "minute", int(m.group('MIN')), "second", int(m.group('SEC'))
+ result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
+ else:
+ raise FpdbError("invalid site in parseHandStartTime")
+
+ if (site=="ftp" or site=="ps") and not isUTC: #these use US ET
+ result+=datetime.timedelta(hours=5)
+
+ return result
+#end def parseHandStartTime
+
+#parses the names out of the given lines and returns them as an array
+def parseNames(lines):
+ result = []
+ for i in range (len(lines)):
+ pos1=lines[i].find(":")+2
+ pos2=lines[i].rfind("(")-1
+ tmp=lines[i][pos1:pos2]
+ #print "parseNames, tmp original:",tmp
+ tmp=unicode(tmp,"latin-1")
+ #print "parseNames, tmp after unicode latin-1 conversion:",tmp
+ result.append(tmp)
+ return result
+#end def parseNames
+
+#returns an array with the positions of the respective players
+def parsePositions (hand, names):
+ #prep array
+ positions=[]
+ for i in range(len(names)):
+ positions.append(-1)
+
+ #find blinds
+ sb,bb=-1,-1
+ for i in range (len(hand)):
+ if (sb==-1 and hand[i].find("small blind")!=-1 and hand[i].find("dead small blind")==-1):
+ sb=hand[i]
+ #print "sb:",sb
+ if (bb==-1 and hand[i].find("big blind")!=-1 and hand[i].find("dead big blind")==-1):
+ bb=hand[i]
+ #print "bb:",bb
+
+ #identify blinds
+ #print "parsePositions before recognising sb/bb. names:",names
+ sbExists=True
+ if (sb!=-1):
+ sb=recognisePlayerNo(sb, names, "bet")
+ else:
+ sbExists=False
+ if (bb!=-1):
+ bb=recognisePlayerNo(bb, names, "bet")
+
+# print "sb = ", sb, "bb = ", bb
+ if bb == sb:
+ sbExists = False
+ sb = -1
+
+ #write blinds into array
+ if (sbExists):
+ positions[sb]="S"
+ positions[bb]="B"
+
+ #fill up rest of array
+ if (sbExists):
+ arraypos=sb-1
+ else:
+ arraypos=bb-1
+ distFromBtn=0
+ while (arraypos>=0 and arraypos != bb):
+ #print "parsePositions first while, arraypos:",arraypos,"positions:",positions
+ positions[arraypos]=distFromBtn
+ arraypos-=1
+ distFromBtn+=1
+ # eric - this takes into account dead seats between blinds
+ if sbExists:
+ i = bb - 1
+ while positions[i] < 0 and i != sb:
+ positions[i] = 9
+ i -= 1
+ ### RHH - Changed to set the null seats before BB to "9"
+ if sbExists:
+ i = sb-1
+ else:
+ i = bb-1
+
+ while positions[i] < 0:
+ positions[i]=9
+ i-=1
+
+ arraypos=len(names)-1
+ if (bb!=0 or (bb==0 and sbExists==False) or (bb == 1 and sb != arraypos) ):
+ while (arraypos>bb and arraypos > sb):
+ positions[arraypos]=distFromBtn
+ arraypos-=1
+ distFromBtn+=1
+
+ for i in range (len(names)):
+ if positions[i]==-1:
+ print "parsePositions names:",names
+ print "result:",positions
+ raise FpdbError ("failed to read positions")
+ #print str(positions), "\n"
+ return positions
+#end def parsePositions
+
+#simply parses the rake amount and returns it as an int
+def parseRake(line):
+ pos=line.find("Rake")+6
+ rake=float2int(line[pos:])
+ return rake
+#end def parseRake
+
+def parseSiteHandNo(topline):
+ """returns the hand no assigned by the poker site"""
+ pos1=topline.find("#")+1
+ pos2=topline.find(":")
+ return topline[pos1:pos2]
+#end def parseSiteHandNo
+
+def parseTableLine(site, base, line):
+ """returns a dictionary with maxSeats and tableName"""
+ if site=="ps":
+ pos1=line.find('\'')+1
+ pos2=line.find('\'', pos1)
+ #print "table:",line[pos1:pos2]
+ pos3=pos2+2
+ pos4=line.find("-max")
+ #print "seats:",line[pos3:pos4]
+ return {'maxSeats':int(line[pos3:pos4]), 'tableName':line[pos1:pos2]}
+ elif site=="ftp":
+ pos1=line.find("Table ")+6
+ pos2=line.find("-")-1
+ if base=="hold":
+ maxSeats=9
+ elif base=="stud":
+ maxSeats=8
+
+ if line.find("6 max")!=-1:
+ maxSeats=6
+ elif line.find("4 max")!=-1:
+ maxSeats=4
+ elif line.find("heads up")!=-1:
+ maxSeats=2
+
+ return {'maxSeats':maxSeats, 'tableName':line[pos1:pos2]}
+ else:
+ raise FpdbError("invalid site ID")
+#end def parseTableLine
+
+#returns the hand no assigned by the poker site
+def parseTourneyNo(topline):
+ pos1=topline.find("Tournament #")+12
+ pos2=topline.find(",", pos1)
+ #print "parseTourneyNo pos1:",pos1," pos2:",pos2, " result:",topline[pos1:pos2]
+ return topline[pos1:pos2]
+#end def parseTourneyNo
+
+#parses a win/collect line. manipulates the passed array winnings, no explicit return
+def parseWinLine(line, site, names, winnings, isTourney):
+ #print "parseWinLine: line:",line
+ for i in range(len(names)):
+ if (line.startswith(names[i].encode("latin-1"))): #found a winner
+ if isTourney:
+ pos1=line.rfind("collected ")+10
+ if (site=="ftp"):
+ pos2=line.find(")", pos1)
+ elif (site=="ps"):
+ pos2=line.find(" ", pos1)
+ winnings[i]+=int(line[pos1:pos2])
+ else:
+ pos1=line.rfind("$")+1
+ if (site=="ftp"):
+ pos2=line.find(")", pos1)
+ elif (site=="ps"):
+ pos2=line.find(" ", pos1)
+ winnings[i]+=float2int(line[pos1:pos2])
+#end def parseWinLine
+
+#returns the category (as per database) string for the given line
+def recogniseCategory(line):
+ if (line.find("Razz")!=-1):
+ return "razz"
+ elif (line.find("Hold'em")!=-1):
+ return "holdem"
+ elif (line.find("Omaha")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1):
+ return "omahahi"
+ elif (line.find("Omaha")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)):
+ return "omahahilo"
+ elif (line.find("Stud")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1):
+ return "studhi"
+ elif (line.find("Stud")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)):
+ return "studhilo"
+ else:
+ raise FpdbError("failed to recognise category, line:"+line)
+#end def recogniseCategory
+
+#returns the int for the gametype_id for the given line
+def recogniseGametypeID(cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy
+ #if (topline.find("HORSE")!=-1):
+ # raise FpdbError("recogniseGametypeID: HORSE is not yet supported.")
+
+ #note: the below variable names small_bet and big_bet are misleading, in NL/PL they mean small/big blind
+ if isTourney:
+ type="tour"
+ pos1=topline.find("(")+1
+ if (topline[pos1]=="H" or topline[pos1]=="O" or topline[pos1]=="R" or topline[pos1]=="S" or topline[pos1+2]=="C"):
+ pos1=topline.find("(", pos1)+1
+ pos2=topline.find("/", pos1)
+ small_bet=int(topline[pos1:pos2])
+ else:
+ type="ring"
+ pos1=topline.find("$")+1
+ pos2=topline.find("/$")
+ small_bet=float2int(topline[pos1:pos2])
+
+ pos1=pos2+2
+ if isTourney:
+ pos1-=1
+ if (site_id==1): #ftp
+ pos2=topline.find(" ", pos1)
+ elif (site_id==2): #ps
+ pos2=topline.find(")")
+
+ if pos2<=pos1:
+ pos2=topline.find(")", pos1)
+
+ if isTourney:
+ big_bet=int(topline[pos1:pos2])
+ else:
+ big_bet=float2int(topline[pos1:pos2])
+
+ if (topline.find("No Limit")!=-1):
+ limit_type="nl"
+ if (topline.find("Cap No")!=-1):
+ limit_type="cn"
+ elif (topline.find("Pot Limit")!=-1):
+ limit_type="pl"
+ if (topline.find("Cap Pot")!=-1):
+ limit_type="cp"
+ else:
+ limit_type="fl"
+
+ #print "recogniseGametypeID small_bet/blind:",small_bet,"big bet/blind:", big_bet,"limit type:",limit_type
+ if (limit_type=="fl"):
+ cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
+ else:
+ cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
+ result=cursor.fetchone()
+ #print "tried SELECTing gametypes.id, result:",result
+
+ try:
+ len(result)
+ except TypeError:
+ if category=="holdem" or category=="omahahi" or category=="omahahilo":
+ base="hold"
+ else:
+ base="stud"
+
+ if category=="holdem" or category=="omahahi" or category=="studhi":
+ hiLo='h'
+ elif category=="razz":
+ hiLo='l'
+ else:
+ hiLo='s'
+
+ if (limit_type=="fl"):
+ big_blind=small_bet
+ if base=="hold":
+ if smallBlindLine==topline:
+ raise FpdbError("invalid small blind line")
+ elif isTourney:
+ pos=smallBlindLine.rfind(" ")+1
+ small_blind=int(smallBlindLine[pos:])
+ else:
+ pos=smallBlindLine.rfind("$")+1
+ small_blind=float2int(smallBlindLine[pos:])
+ else:
+ small_blind=0
+ cursor.execute("""INSERT INTO Gametypes
+ (siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_blind, big_blind, small_bet, big_bet))
+ cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
+ else:
+ cursor.execute("""INSERT INTO Gametypes
+ (siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_bet, big_bet, 0, 0))#remember, for these bet means blind
+ cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
+
+ result=cursor.fetchone()
+ #print "created new gametypes.id:",result
+
+ return result[0]
+#end def recogniseGametypeID
+
+def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon):
+ cursor.execute ("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
+ result=cursor.fetchone()
+ #print "tried SELECTing gametypes.id, result:",result
+
+ try:
+ len(result)
+ except TypeError:#this means we need to create a new entry
+ cursor.execute("""INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) VALUES (%s, %s, %s, %s, %s)""", (siteId, buyin, fee, knockout, rebuyOrAddon))
+ cursor.execute("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
+ result=cursor.fetchone()
+ return result[0]
+#end def recogniseTourneyTypeId
+
+#returns the SQL ids of the names given in an array
+def recognisePlayerIDs(cursor, names, site_id):
+ result = []
+ for i in range (len(names)):
+ cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],))
+ tmp=cursor.fetchall()
+ if (len(tmp)==0): #new player
+ cursor.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (names[i], site_id))
+ #print "Number of players rows inserted: %d" % cursor.rowcount
+ cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],))
+ tmp=cursor.fetchall()
+ #print "recognisePlayerIDs, names[i]:",names[i],"tmp:",tmp
+ result.append(tmp[0][0])
+ return result
+#end def recognisePlayerIDs
+
+#recognises the name in the given line and returns its array position in the given array
+def recognisePlayerNo(line, names, atype):
+ #print "recogniseplayerno, names:",names
+ for i in range (len(names)):
+ if (atype=="unbet"):
+ if (line.endswith(names[i].encode("latin-1"))):
+ return (i)
+ elif (line.startswith("Dealt to ")):
+ #print "recognisePlayerNo, card precut, line:",line
+ tmp=line[9:]
+ #print "recognisePlayerNo, card postcut, tmp:",tmp
+ if (tmp.startswith(names[i].encode("latin-1"))):
+ return (i)
+ elif (line.startswith("Seat ")):
+ if (line.startswith("Seat 10")):
+ tmp=line[9:]
+ else:
+ tmp=line[8:]
+
+ if (tmp.startswith(names[i].encode("latin-1"))):
+ return (i)
+ else:
+ if (line.startswith(names[i].encode("latin-1"))):
+ return (i)
+ #if we're here we mustve failed
+ raise FpdbError ("failed to recognise player in: "+line+" atype:"+atype)
+#end def recognisePlayerNo
+
+#returns the site abbreviation for the given site
+def recogniseSite(line):
+ if (line.startswith("Full Tilt Poker")):
+ return "ftp"
+ elif (line.startswith("PokerStars")):
+ return "ps"
+ else:
+ raise FpdbError("failed to recognise site, line:"+line)
+#end def recogniseSite
+
+#returns the ID of the given site
+def recogniseSiteID(cursor, site):
+ if (site=="ftp"):
+ return 1
+ #cursor.execute("SELECT id FROM Sites WHERE name = ('Full Tilt Poker')")
+ elif (site=="ps"):
+ return 2
+ #cursor.execute("SELECT id FROM Sites WHERE name = ('PokerStars')")
+ else:
+ raise FpdbError("invalid site in recogniseSiteID: "+site)
+ return cursor.fetchall()[0][0]
+#end def recogniseSiteID
+
+#removes trailing \n from the given array
+def removeTrailingEOL(arr):
+ for i in range(len(arr)):
+ if (arr[i].endswith("\n")):
+ #print "arr[i] before removetrailingEOL:", arr[i]
+ arr[i]=arr[i][:-1]
+ #print "arr[i] after removetrailingEOL:", arr[i]
+ return arr
+#end def removeTrailingEOL
+
+#splits the rake according to the proportion of pot won. manipulates the second passed array.
+def splitRake(winnings, rakes, totalRake):
+ winnercnt=0
+ totalWin=0
+ for i in range(len(winnings)):
+ if winnings[i]!=0:
+ winnercnt+=1
+ totalWin+=winnings[i]
+ firstWinner=i
+ if winnercnt==1:
+ rakes[firstWinner]=totalRake
+ else:
+ totalWin=float(totalWin)
+ for i in range(len(winnings)):
+ if winnings[i]!=0:
+ winPortion=winnings[i]/totalWin
+ rakes[i]=totalRake*winPortion
+#end def splitRake
+
+def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, actionNos):
+#stores into table hands_actions
+ #print "start of storeActions, actionNos:",actionNos
+ #print " action_amounts:",action_amounts
+ for i in range (len(actionTypes)): #iterate through streets
+ for j in range (len(actionTypes[i])): #iterate through names
+ for k in range (len(actionTypes[i][j])): #iterate through individual actions of that player on that street
+ cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k]))
+#end def storeActions
+
+def store_board_cards(cursor, hands_id, board_values, board_suits):
+#stores into table board_cards
+ cursor.execute ("""INSERT INTO BoardCards (handId, card1Value, card1Suit,
+ card2Value, card2Suit, card3Value, card3Suit, card4Value, card4Suit,
+ card5Value, card5Suit) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
+ (hands_id, board_values[0], board_suits[0], board_values[1], board_suits[1],
+ board_values[2], board_suits[2], board_values[3], board_suits[3],
+ board_values[4], board_suits[4]))
+#end def store_board_cards
+
+def storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats):
+#stores into table hands
+ cursor.execute ("INSERT INTO Hands (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats) VALUES (%s, %s, %s, %s, %s, %s, %s)", (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats))
+ #todo: find a better way of doing this...
+ cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id))
+ return cursor.fetchall()[0][0]
+#end def storeHands
+
+def store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos):
+ result=[]
+ if (category=="holdem"):
+ for i in range (len(player_ids)):
+ cursor.execute ("""
+ INSERT INTO HandsPlayers
+ (handId, playerId, startCash, position,
+ card1Value, card1Suit, card2Value, card2Suit, winnings, rake, seatNo)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
+ (hands_id, player_ids[i], start_cashes[i], positions[i],
+ card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
+ winnings[i], rakes[i], seatNos[i]))
+ cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
+ result.append(cursor.fetchall()[0][0])
+ elif (category=="omahahi" or category=="omahahilo"):
+ for i in range (len(player_ids)):
+ cursor.execute ("""INSERT INTO HandsPlayers
+ (handId, playerId, startCash, position,
+ card1Value, card1Suit, card2Value, card2Suit,
+ card3Value, card3Suit, card4Value, card4Suit, winnings, rake, seatNo)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
+ (hands_id, player_ids[i], start_cashes[i], positions[i],
+ card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
+ card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
+ winnings[i], rakes[i], seatNos[i]))
+ cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
+ result.append(cursor.fetchall()[0][0])
+ else:
+ raise FpdbError("invalid category")
+ return result
+#end def store_hands_players_holdem_omaha
+
+def store_hands_players_stud(cursor, hands_id, player_ids, start_cashes, antes,
+ card_values, card_suits, winnings, rakes, seatNos):
+#stores hands_players rows for stud/razz games. returns an array of the resulting IDs
+ result=[]
+ #print "before inserts in store_hands_players_stud, antes:", antes
+ for i in range (len(player_ids)):
+ cursor.execute ("""INSERT INTO HandsPlayers
+ (handId, playerId, startCash, ante,
+ card1Value, card1Suit, card2Value, card2Suit,
+ card3Value, card3Suit, card4Value, card4Suit,
+ card5Value, card5Suit, card6Value, card6Suit,
+ card7Value, card7Suit, winnings, rake, seatNo)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
+ %s, %s, %s, %s)""",
+ (hands_id, player_ids[i], start_cashes[i], antes[i],
+ card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
+ card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
+ card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
+ card_values[i][6], card_suits[i][6], winnings[i], rakes[i], seatNos[i]))
+ cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
+ result.append(cursor.fetchall()[0][0])
+ return result
+#end def store_hands_players_stud
+
+def store_hands_players_holdem_omaha_tourney(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
+#stores hands_players for tourney holdem/omaha hands
+ result=[]
+ for i in range (len(player_ids)):
+ if len(card_values[0])==2:
+ cursor.execute ("""INSERT INTO HandsPlayers
+ (handId, playerId, startCash, position,
+ card1Value, card1Suit, card2Value, card2Suit,
+ winnings, rake, tourneysPlayersId, seatNo)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
+ (hands_id, player_ids[i], start_cashes[i], positions[i],
+ card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
+ winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
+ elif len(card_values[0])==4:
+ cursor.execute ("""INSERT INTO HandsPlayers
+ (handId, playerId, startCash, position,
+ card1Value, card1Suit, card2Value, card2Suit,
+ card3Value, card3Suit, card4Value, card4Suit,
+ winnings, rake, tourneysPlayersId, seatNo)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
+ (hands_id, player_ids[i], start_cashes[i], positions[i],
+ card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
+ card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
+ winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
+ else:
+ raise FpdbError ("invalid card_values length:"+str(len(card_values[0])))
+ cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
+ result.append(cursor.fetchall()[0][0])
+
+ return result
+#end def store_hands_players_holdem_omaha_tourney
+
+def store_hands_players_stud_tourney(cursor, hands_id, player_ids, start_cashes,
+ antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
+#stores hands_players for tourney stud/razz hands
+ result=[]
+ for i in range (len(player_ids)):
+ cursor.execute ("""INSERT INTO HandsPlayers
+ (handId, playerId, startCash, ante,
+ card1Value, card1Suit, card2Value, card2Suit,
+ card3Value, card3Suit, card4Value, card4Suit,
+ card5Value, card5Suit, card6Value, card6Suit,
+ card7Value, card7Suit, winnings, rake, tourneysPlayersId, seatNo)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s, %s)""",
+ (hands_id, player_ids[i], start_cashes[i], antes[i],
+ card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
+ card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
+ card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
+ card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
+ cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i]))
+ result.append(cursor.fetchall()[0][0])
+ return result
+#end def store_hands_players_stud_tourney
+
+def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo, winnings, totalWinnings, positions):
+ """calculates data for the HUD during import. IMPORTANT: if you change this method make sure to also change the following storage method and table_viewer.prepare_data if necessary"""
+ #setup subarrays of the result dictionary.
+ street0VPI=[]
+ street0Aggr=[]
+ street0_3B4BChance=[]
+ street0_3B4BDone=[]
+ street1Seen=[]
+ street2Seen=[]
+ street3Seen=[]
+ street4Seen=[]
+ sawShowdown=[]
+ street1Aggr=[]
+ street2Aggr=[]
+ street3Aggr=[]
+ street4Aggr=[]
+ otherRaisedStreet1=[]
+ otherRaisedStreet2=[]
+ otherRaisedStreet3=[]
+ otherRaisedStreet4=[]
+ foldToOtherRaisedStreet1=[]
+ foldToOtherRaisedStreet2=[]
+ foldToOtherRaisedStreet3=[]
+ foldToOtherRaisedStreet4=[]
+ wonWhenSeenStreet1=[]
+
+ wonAtSD=[]
+ stealAttemptChance=[]
+ stealAttempted=[]
+ hudDataPositions=[]
+
+ firstPfRaiseByNo=-1
+ firstPfRaiserId=-1
+ firstPfRaiserNo=-1
+ firstPfCallByNo=-1
+ firstPfCallerId=-1
+ for i in range(len(actionTypeByNo[0])):
+ if actionTypeByNo[0][i][1]=="bet":
+ firstPfRaiseByNo=i
+ firstPfRaiserId=actionTypeByNo[0][i][0]
+ for j in range(len(player_ids)):
+ if player_ids[j]==firstPfRaiserId:
+ firstPfRaiserNo=j
+ break
+ break
+ for i in range(len(actionTypeByNo[0])):
+ if actionTypeByNo[0][i][1]=="call":
+ firstPfCallByNo=i
+ firstPfCallerId=actionTypeByNo[0][i][0]
+ break
+
+ cutoffId=-1
+ buttonId=-1
+ sbId=-1
+ bbId=-1
+ if base=="hold":
+ for player in range(len(positions)):
+ if positions==1:
+ cutoffId=player_ids[player]
+ if positions==0:
+ buttonId=player_ids[player]
+ if positions=='S':
+ sbId=player_ids[player]
+ if positions=='B':
+ bbId=player_ids[player]
+
+ someoneStole=False
+
+ #run a loop for each player preparing the actual values that will be commited to SQL
+ for player in range (len(player_ids)):
+ #set default values
+ myStreet0VPI=False
+ myStreet0Aggr=False
+ myStreet0_3B4BChance=False
+ myStreet0_3B4BDone=False
+ myStreet1Seen=False
+ myStreet2Seen=False
+ myStreet3Seen=False
+ myStreet4Seen=False
+ mySawShowdown=False
+ myStreet1Aggr=False
+ myStreet2Aggr=False
+ myStreet3Aggr=False
+ myStreet4Aggr=False
+ myOtherRaisedStreet1=False
+ myOtherRaisedStreet2=False
+ myOtherRaisedStreet3=False
+ myOtherRaisedStreet4=False
+ myFoldToOtherRaisedStreet1=False
+ myFoldToOtherRaisedStreet2=False
+ myFoldToOtherRaisedStreet3=False
+ myFoldToOtherRaisedStreet4=False
+ myWonWhenSeenStreet1=0.0
+ myWonAtSD=0.0
+ myStealAttemptChance=False
+ myStealAttempted=False
+
+ #calculate VPIP and PFR
+ street=0
+ heroPfRaiseCount=0
+ for count in range (len(action_types[street][player])):#finally individual actions
+ currentAction=action_types[street][player][count]
+ if currentAction=="bet":
+ myStreet0Aggr=True
+ if (currentAction=="bet" or currentAction=="call"):
+ myStreet0VPI=True
+
+ #PF3B4BChance and PF3B4B
+ pfFold=-1
+ pfRaise=-1
+ if firstPfRaiseByNo!=-1:
+ for i in range(len(actionTypeByNo[0])):
+ if actionTypeByNo[0][i][0]==player_ids[player]:
+ if actionTypeByNo[0][i][1]=="bet" and pfRaise==-1 and i>firstPfRaiseByNo:
+ pfRaise=i
+ if actionTypeByNo[0][i][1]=="fold" and pfFold==-1:
+ pfFold=i
+ if pfFold==-1 or pfFold>firstPfRaiseByNo:
+ myStreet0_3B4BChance=True
+ if pfRaise>firstPfRaiseByNo:
+ myStreet0_3B4BDone=True
+
+ #steal calculations
+ if base=="hold":
+ if len(player_ids)>=5: #no point otherwise
+ if positions[player]==1:
+ if firstPfRaiserId==player_ids[player]:
+ myStealAttemptChance=True
+ myStealAttempted=True
+ elif firstPfRaiserId==buttonId or firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1:
+ myStealAttemptChance=True
+ if positions[player]==0:
+ if firstPfRaiserId==player_ids[player]:
+ myStealAttemptChance=True
+ myStealAttempted=True
+ elif firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1:
+ myStealAttemptChance=True
+ if positions[player]=='S':
+ if firstPfRaiserId==player_ids[player]:
+ myStealAttemptChance=True
+ myStealAttempted=True
+ elif firstPfRaiserId==bbId or firstPfRaiserId==-1:
+ myStealAttemptChance=True
+ if positions[player]=='B':
+ pass
+
+ if myStealAttempted:
+ someoneStole=True
+
+
+ #calculate saw* values
+ isAllIn=False
+ for i in range(len(allIns[0][player])):
+ if allIns[0][player][i]:
+ isAllIn=True
+ if (len(action_types[1][player])>0 or isAllIn):
+ myStreet1Seen=True
+
+ for i in range(len(allIns[1][player])):
+ if allIns[1][player][i]:
+ isAllIn=True
+ if (len(action_types[2][player])>0 or isAllIn):
+ myStreet2Seen=True
+
+ for i in range(len(allIns[2][player])):
+ if allIns[2][player][i]:
+ isAllIn=True
+ if (len(action_types[3][player])>0 or isAllIn):
+ myStreet3Seen=True
+
+ #print "base:", base
+ if base=="hold":
+ mySawShowdown=True
+ for count in range (len(action_types[3][player])):
+ if action_types[3][player][count]=="fold":
+ mySawShowdown=False
+ else:
+ #print "in else"
+ for i in range(len(allIns[3][player])):
+ if allIns[3][player][i]:
+ isAllIn=True
+ if (len(action_types[4][player])>0 or isAllIn):
+ #print "in if"
+ myStreet4Seen=True
+
+ mySawShowdown=True
+ for count in range (len(action_types[4][player])):
+ if action_types[4][player][count]=="fold":
+ mySawShowdown=False
+
+
+ #flop stuff
+ street=1
+ if myStreet1Seen:
+ for count in range(len(action_types[street][player])):
+ if action_types[street][player][count]=="bet":
+ myStreet1Aggr=True
+
+ for otherPlayer in range (len(player_ids)):
+ if player==otherPlayer:
+ pass
+ else:
+ for countOther in range (len(action_types[street][otherPlayer])):
+ if action_types[street][otherPlayer][countOther]=="bet":
+ myOtherRaisedStreet1=True
+ for countOtherFold in range (len(action_types[street][player])):
+ if action_types[street][player][countOtherFold]=="fold":
+ myFoldToOtherRaisedStreet1=True
+
+ #turn stuff - copy of flop with different vars
+ street=2
+ if myStreet2Seen:
+ for count in range(len(action_types[street][player])):
+ if action_types[street][player][count]=="bet":
+ myStreet2Aggr=True
+
+ for otherPlayer in range (len(player_ids)):
+ if player==otherPlayer:
+ pass
+ else:
+ for countOther in range (len(action_types[street][otherPlayer])):
+ if action_types[street][otherPlayer][countOther]=="bet":
+ myOtherRaisedStreet2=True
+ for countOtherFold in range (len(action_types[street][player])):
+ if action_types[street][player][countOtherFold]=="fold":
+ myFoldToOtherRaisedStreet2=True
+
+ #river stuff - copy of flop with different vars
+ street=3
+ if myStreet3Seen:
+ for count in range(len(action_types[street][player])):
+ if action_types[street][player][count]=="bet":
+ myStreet3Aggr=True
+
+ for otherPlayer in range (len(player_ids)):
+ if player==otherPlayer:
+ pass
+ else:
+ for countOther in range (len(action_types[street][otherPlayer])):
+ if action_types[street][otherPlayer][countOther]=="bet":
+ myOtherRaisedStreet3=True
+ for countOtherFold in range (len(action_types[street][player])):
+ if action_types[street][player][countOtherFold]=="fold":
+ myFoldToOtherRaisedStreet3=True
+
+ #stud river stuff - copy of flop with different vars
+ street=4
+ if myStreet4Seen:
+ for count in range(len(action_types[street][player])):
+ if action_types[street][player][count]=="bet":
+ myStreet4Aggr=True
+
+ for otherPlayer in range (len(player_ids)):
+ if player==otherPlayer:
+ pass
+ else:
+ for countOther in range (len(action_types[street][otherPlayer])):
+ if action_types[street][otherPlayer][countOther]=="bet":
+ myOtherRaisedStreet4=True
+ for countOtherFold in range (len(action_types[street][player])):
+ if action_types[street][player][countOtherFold]=="fold":
+ myFoldToOtherRaisedStreet4=True
+
+ if winnings[player]!=0:
+ if myStreet1Seen:
+ myWonWhenSeenStreet1=winnings[player]/float(totalWinnings)
+ if mySawShowdown:
+ myWonAtSD=myWonWhenSeenStreet1
+
+ #add each value to the appropriate array
+ street0VPI.append(myStreet0VPI)
+ street0Aggr.append(myStreet0Aggr)
+ street0_3B4BChance.append(myStreet0_3B4BChance)
+ street0_3B4BDone.append(myStreet0_3B4BDone)
+ street1Seen.append(myStreet1Seen)
+ street2Seen.append(myStreet2Seen)
+ street3Seen.append(myStreet3Seen)
+ street4Seen.append(myStreet4Seen)
+ sawShowdown.append(mySawShowdown)
+ street1Aggr.append(myStreet1Aggr)
+ street2Aggr.append(myStreet2Aggr)
+ street3Aggr.append(myStreet3Aggr)
+ street4Aggr.append(myStreet4Aggr)
+ otherRaisedStreet1.append(myOtherRaisedStreet1)
+ otherRaisedStreet2.append(myOtherRaisedStreet2)
+ otherRaisedStreet3.append(myOtherRaisedStreet3)
+ otherRaisedStreet4.append(myOtherRaisedStreet4)
+ foldToOtherRaisedStreet1.append(myFoldToOtherRaisedStreet1)
+ foldToOtherRaisedStreet2.append(myFoldToOtherRaisedStreet2)
+ foldToOtherRaisedStreet3.append(myFoldToOtherRaisedStreet3)
+ foldToOtherRaisedStreet4.append(myFoldToOtherRaisedStreet4)
+ wonWhenSeenStreet1.append(myWonWhenSeenStreet1)
+ wonAtSD.append(myWonAtSD)
+ stealAttemptChance.append(myStealAttemptChance)
+ stealAttempted.append(myStealAttempted)
+ if base=="hold":
+ pos=positions[player]
+ if pos=='B':
+ hudDataPositions.append('B')
+ elif pos=='S':
+ hudDataPositions.append('S')
+ elif pos==0:
+ hudDataPositions.append('D')
+ elif pos==1:
+ hudDataPositions.append('C')
+ elif pos>=2 and pos<=4:
+ hudDataPositions.append('M')
+ elif pos>=5 and pos<=8:
+ hudDataPositions.append('E')
+ ### RHH Added this elif to handle being a dead hand before the BB (pos==9)
+ elif pos==9:
+ hudDataPositions.append('X')
+ else:
+ raise FpdbError("invalid position")
+ elif base=="stud":
+ #todo: stud positions and steals
+ pass
+
+ #add each array to the to-be-returned dictionary
+ result={'street0VPI':street0VPI}
+ result['street0Aggr']=street0Aggr
+ result['street0_3B4BChance']=street0_3B4BChance
+ result['street0_3B4BDone']=street0_3B4BDone
+ result['street1Seen']=street1Seen
+ result['street2Seen']=street2Seen
+ result['street3Seen']=street3Seen
+ result['street4Seen']=street4Seen
+ result['sawShowdown']=sawShowdown
+
+ result['street1Aggr']=street1Aggr
+ result['otherRaisedStreet1']=otherRaisedStreet1
+ result['foldToOtherRaisedStreet1']=foldToOtherRaisedStreet1
+ result['street2Aggr']=street2Aggr
+ result['otherRaisedStreet2']=otherRaisedStreet2
+ result['foldToOtherRaisedStreet2']=foldToOtherRaisedStreet2
+ result['street3Aggr']=street3Aggr
+ result['otherRaisedStreet3']=otherRaisedStreet3
+ result['foldToOtherRaisedStreet3']=foldToOtherRaisedStreet3
+ result['street4Aggr']=street4Aggr
+ result['otherRaisedStreet4']=otherRaisedStreet4
+ result['foldToOtherRaisedStreet4']=foldToOtherRaisedStreet4
+ result['wonWhenSeenStreet1']=wonWhenSeenStreet1
+ result['wonAtSD']=wonAtSD
+ result['stealAttemptChance']=stealAttemptChance
+ result['stealAttempted']=stealAttempted
+
+ #now the various steal values
+ foldBbToStealChance=[]
+ foldedBbToSteal=[]
+ foldSbToStealChance=[]
+ foldedSbToSteal=[]
+ for player in range (len(player_ids)):
+ myFoldBbToStealChance=False
+ myFoldedBbToSteal=False
+ myFoldSbToStealChance=False
+ myFoldedSbToSteal=False
+
+ if base=="hold":
+ if someoneStole and (positions[player]=='B' or positions[player]=='S') and firstPfRaiserId!=player_ids[player]:
+ street=0
+ for count in range (len(action_types[street][player])):#individual actions
+ if positions[player]=='B':
+ myFoldBbToStealChance=True
+ if action_types[street][player][count]=="fold":
+ myFoldedBbToSteal=True
+ if positions[player]=='S':
+ myFoldSbToStealChance=True
+ if action_types[street][player][count]=="fold":
+ myFoldedSbToSteal=True
+
+
+ foldBbToStealChance.append(myFoldBbToStealChance)
+ foldedBbToSteal.append(myFoldedBbToSteal)
+ foldSbToStealChance.append(myFoldSbToStealChance)
+ foldedSbToSteal.append(myFoldedSbToSteal)
+ result['foldBbToStealChance']=foldBbToStealChance
+ result['foldedBbToSteal']=foldedBbToSteal
+ result['foldSbToStealChance']=foldSbToStealChance
+ result['foldedSbToSteal']=foldedSbToSteal
+
+ #now CB
+ street1CBChance=[]
+ street1CBDone=[]
+ didStreet1CB=[]
+ for player in range (len(player_ids)):
+ myStreet1CBChance=False
+ myStreet1CBDone=False
+
+ if street0VPI[player]:
+ myStreet1CBChance=True
+ if street1Aggr[player]:
+ myStreet1CBDone=True
+ didStreet1CB.append(player_ids[player])
+
+ street1CBChance.append(myStreet1CBChance)
+ street1CBDone.append(myStreet1CBDone)
+ result['street1CBChance']=street1CBChance
+ result['street1CBDone']=street1CBDone
+
+ #now 2B
+ street2CBChance=[]
+ street2CBDone=[]
+ didStreet2CB=[]
+ for player in range (len(player_ids)):
+ myStreet2CBChance=False
+ myStreet2CBDone=False
+
+ if street1CBDone[player]:
+ myStreet2CBChance=True
+ if street2Aggr[player]:
+ myStreet2CBDone=True
+ didStreet2CB.append(player_ids[player])
+
+ street2CBChance.append(myStreet2CBChance)
+ street2CBDone.append(myStreet2CBDone)
+ result['street2CBChance']=street2CBChance
+ result['street2CBDone']=street2CBDone
+
+ #now 3B
+ street3CBChance=[]
+ street3CBDone=[]
+ didStreet3CB=[]
+ for player in range (len(player_ids)):
+ myStreet3CBChance=False
+ myStreet3CBDone=False
+
+ if street2CBDone[player]:
+ myStreet3CBChance=True
+ if street3Aggr[player]:
+ myStreet3CBDone=True
+ didStreet3CB.append(player_ids[player])
+
+ street3CBChance.append(myStreet3CBChance)
+ street3CBDone.append(myStreet3CBDone)
+ result['street3CBChance']=street3CBChance
+ result['street3CBDone']=street3CBDone
+
+ #and 4B
+ street4CBChance=[]
+ street4CBDone=[]
+ didStreet4CB=[]
+ for player in range (len(player_ids)):
+ myStreet4CBChance=False
+ myStreet4CBDone=False
+
+ if street3CBDone[player]:
+ myStreet4CBChance=True
+ if street4Aggr[player]:
+ myStreet4CBDone=True
+ didStreet4CB.append(player_ids[player])
+
+ street4CBChance.append(myStreet4CBChance)
+ street4CBDone.append(myStreet4CBDone)
+ result['street4CBChance']=street4CBChance
+ result['street4CBDone']=street4CBDone
+
+
+ result['position']=hudDataPositions
+
+ foldToStreet1CBChance=[]
+ foldToStreet1CBDone=[]
+ foldToStreet2CBChance=[]
+ foldToStreet2CBDone=[]
+ foldToStreet3CBChance=[]
+ foldToStreet3CBDone=[]
+ foldToStreet4CBChance=[]
+ foldToStreet4CBDone=[]
+
+ for player in range (len(player_ids)):
+ myFoldToStreet1CBChance=False
+ myFoldToStreet1CBDone=False
+ foldToStreet1CBChance.append(myFoldToStreet1CBChance)
+ foldToStreet1CBDone.append(myFoldToStreet1CBDone)
+
+ myFoldToStreet2CBChance=False
+ myFoldToStreet2CBDone=False
+ foldToStreet2CBChance.append(myFoldToStreet2CBChance)
+ foldToStreet2CBDone.append(myFoldToStreet2CBDone)
+
+ myFoldToStreet3CBChance=False
+ myFoldToStreet3CBDone=False
+ foldToStreet3CBChance.append(myFoldToStreet3CBChance)
+ foldToStreet3CBDone.append(myFoldToStreet3CBDone)
+
+ myFoldToStreet4CBChance=False
+ myFoldToStreet4CBDone=False
+ foldToStreet4CBChance.append(myFoldToStreet4CBChance)
+ foldToStreet4CBDone.append(myFoldToStreet4CBDone)
+
+ if len(didStreet1CB)>=1:
+ generateFoldToCB(1, player_ids, didStreet1CB, street1CBDone, foldToStreet1CBChance, foldToStreet1CBDone, actionTypeByNo)
+
+ if len(didStreet2CB)>=1:
+ generateFoldToCB(2, player_ids, didStreet2CB, street2CBDone, foldToStreet2CBChance, foldToStreet2CBDone, actionTypeByNo)
+
+ if len(didStreet3CB)>=1:
+ generateFoldToCB(3, player_ids, didStreet3CB, street3CBDone, foldToStreet3CBChance, foldToStreet3CBDone, actionTypeByNo)
+
+ if len(didStreet4CB)>=1:
+ generateFoldToCB(4, player_ids, didStreet4CB, street4CBDone, foldToStreet4CBChance, foldToStreet4CBDone, actionTypeByNo)
+
+ result['foldToStreet1CBChance']=foldToStreet1CBChance
+ result['foldToStreet1CBDone']=foldToStreet1CBDone
+ result['foldToStreet2CBChance']=foldToStreet2CBChance
+ result['foldToStreet2CBDone']=foldToStreet2CBDone
+ result['foldToStreet3CBChance']=foldToStreet3CBChance
+ result['foldToStreet3CBDone']=foldToStreet3CBDone
+ result['foldToStreet4CBChance']=foldToStreet4CBChance
+ result['foldToStreet4CBDone']=foldToStreet4CBDone
+
+
+ totalProfit=[]
+
+ street1CheckCallRaiseChance=[]
+ street1CheckCallRaiseDone=[]
+ street2CheckCallRaiseChance=[]
+ street2CheckCallRaiseDone=[]
+ street3CheckCallRaiseChance=[]
+ street3CheckCallRaiseDone=[]
+ street4CheckCallRaiseChance=[]
+ street4CheckCallRaiseDone=[]
+ for player in range (len(player_ids)):
+ myTotalProfit=0
+
+ myStreet1CheckCallRaiseChance=False
+ myStreet1CheckCallRaiseDone=False
+ myStreet2CheckCallRaiseChance=False
+ myStreet2CheckCallRaiseDone=False
+ myStreet3CheckCallRaiseChance=False
+ myStreet3CheckCallRaiseDone=False
+ myStreet4CheckCallRaiseChance=False
+ myStreet4CheckCallRaiseDone=False
+
+ totalProfit.append(myTotalProfit)
+
+ street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance)
+ street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone)
+ street2CheckCallRaiseChance.append(myStreet2CheckCallRaiseChance)
+ street2CheckCallRaiseDone.append(myStreet2CheckCallRaiseDone)
+ street3CheckCallRaiseChance.append(myStreet3CheckCallRaiseChance)
+ street3CheckCallRaiseDone.append(myStreet3CheckCallRaiseDone)
+ street4CheckCallRaiseChance.append(myStreet4CheckCallRaiseChance)
+ street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone)
+
+ result['totalProfit']=totalProfit
+
+ result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance
+ result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone
+ result['street2CheckCallRaiseChance']=street2CheckCallRaiseChance
+ result['street2CheckCallRaiseDone']=street2CheckCallRaiseDone
+ result['street3CheckCallRaiseChance']=street3CheckCallRaiseChance
+ result['street3CheckCallRaiseDone']=street3CheckCallRaiseDone
+ result['street4CheckCallRaiseChance']=street4CheckCallRaiseChance
+ result['street4CheckCallRaiseDone']=street4CheckCallRaiseDone
+ return result
+#end def generateHudCacheData
+
+def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetCBChance, foldToStreetCBDone, actionTypeByNo):
+ """fills the passed foldToStreetCB* arrays appropriately depending on the given street"""
+ #print "beginning of generateFoldToCB, street:", street, "len(actionTypeByNo):", len(actionTypeByNo)
+ #print "len(actionTypeByNo[street]):",len(actionTypeByNo[street])
+ firstCBReaction=0
+ for action in range(len(actionTypeByNo[street])):
+ if actionTypeByNo[street][action][1]=="bet":
+ for player in didStreetCB:
+ if player==actionTypeByNo[street][action][0] and firstCBReaction==0:
+ firstCBReaction=action+1
+ break
+
+ for action in actionTypeByNo[street][firstCBReaction:]:
+ for player in range(len(playerIDs)):
+ if playerIDs[player]==action[0]:
+ foldToStreetCBChance[player]=True
+ if action[1]=="fold":
+ foldToStreetCBDone[player]=True
+#end def generateFoldToCB
+
+def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
+# if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
+
+ for player in range (len(playerIds)):
+ if base=="hold":
+ cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player]))
+ else:
+ cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s", (gametypeId, playerIds[player], len(playerIds)))
+ row=cursor.fetchone()
+ #print "gametypeId:", gametypeId, "playerIds[player]",playerIds[player], "len(playerIds):",len(playerIds), "row:",row
+
+ try: len(row)
+ except TypeError:
+ row=[]
+
+ if (len(row)==0):
+ #print "new huddata row"
+ doInsert=True
+ row=[]
+ row.append(0)#blank for id
+ row.append(gametypeId)
+ row.append(playerIds[player])
+ row.append(len(playerIds))#seats
+ for i in range(len(hudImportData)+2):
+ row.append(0)
+
+ else:
+ doInsert=False
+ newrow=[]
+ for i in range(len(row)):
+ newrow.append(row[i])
+ row=newrow
+
+ if base=="hold":
+ row[4]=hudImportData['position'][player]
+ else:
+ row[4]=0
+ row[5]=1 #tourneysGametypeId
+ row[6]+=1 #HDs
+ if hudImportData['street0VPI'][player]: row[7]+=1
+ if hudImportData['street0Aggr'][player]: row[8]+=1
+ if hudImportData['street0_3B4BChance'][player]: row[9]+=1
+ if hudImportData['street0_3B4BDone'][player]: row[10]+=1
+ if hudImportData['street1Seen'][player]: row[11]+=1
+ if hudImportData['street2Seen'][player]: row[12]+=1
+ if hudImportData['street3Seen'][player]: row[13]+=1
+ if hudImportData['street4Seen'][player]: row[14]+=1
+ if hudImportData['sawShowdown'][player]: row[15]+=1
+ if hudImportData['street1Aggr'][player]: row[16]+=1
+ if hudImportData['street2Aggr'][player]: row[17]+=1
+ if hudImportData['street3Aggr'][player]: row[18]+=1
+ if hudImportData['street4Aggr'][player]: row[19]+=1
+ if hudImportData['otherRaisedStreet1'][player]: row[20]+=1
+ if hudImportData['otherRaisedStreet2'][player]: row[21]+=1
+ if hudImportData['otherRaisedStreet3'][player]: row[22]+=1
+ if hudImportData['otherRaisedStreet4'][player]: row[23]+=1
+ if hudImportData['foldToOtherRaisedStreet1'][player]: row[24]+=1
+ if hudImportData['foldToOtherRaisedStreet2'][player]: row[25]+=1
+ if hudImportData['foldToOtherRaisedStreet3'][player]: row[26]+=1
+ if hudImportData['foldToOtherRaisedStreet4'][player]: row[27]+=1
+ if hudImportData['wonWhenSeenStreet1'][player]!=0.0: row[28]+=hudImportData['wonWhenSeenStreet1'][player]
+ if hudImportData['wonAtSD'][player]!=0.0: row[29]+=hudImportData['wonAtSD'][player]
+ if hudImportData['stealAttemptChance'][player]: row[30]+=1
+ if hudImportData['stealAttempted'][player]: row[31]+=1
+ if hudImportData['foldBbToStealChance'][player]: row[32]+=1
+ if hudImportData['foldedBbToSteal'][player]: row[33]+=1
+ if hudImportData['foldSbToStealChance'][player]: row[34]+=1
+ if hudImportData['foldedSbToSteal'][player]: row[35]+=1
+
+ if hudImportData['street1CBChance'][player]: row[36]+=1
+ if hudImportData['street1CBDone'][player]: row[37]+=1
+ if hudImportData['street2CBChance'][player]: row[38]+=1
+ if hudImportData['street2CBDone'][player]: row[39]+=1
+ if hudImportData['street3CBChance'][player]: row[40]+=1
+ if hudImportData['street3CBDone'][player]: row[41]+=1
+ if hudImportData['street4CBChance'][player]: row[42]+=1
+ if hudImportData['street4CBDone'][player]: row[43]+=1
+
+ if hudImportData['foldToStreet1CBChance'][player]: row[44]+=1
+ if hudImportData['foldToStreet1CBDone'][player]: row[45]+=1
+ if hudImportData['foldToStreet2CBChance'][player]: row[46]+=1
+ if hudImportData['foldToStreet2CBDone'][player]: row[47]+=1
+ if hudImportData['foldToStreet3CBChance'][player]: row[48]+=1
+ if hudImportData['foldToStreet3CBDone'][player]: row[49]+=1
+ if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1
+ if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1
+
+ row[52]+=hudImportData['totalProfit'][player]
+
+ if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1
+ if hudImportData['street1CheckCallRaiseDone'][player]: row[54]+=1
+ if hudImportData['street2CheckCallRaiseChance'][player]: row[55]+=1
+ if hudImportData['street2CheckCallRaiseDone'][player]: row[56]+=1
+ if hudImportData['street3CheckCallRaiseChance'][player]: row[57]+=1
+ if hudImportData['street3CheckCallRaiseDone'][player]: row[58]+=1
+ if hudImportData['street4CheckCallRaiseChance'][player]: row[59]+=1
+ if hudImportData['street4CheckCallRaiseDone'][player]: row[60]+=1
+
+ if doInsert:
+ #print "playerid before insert:",row[2]
+ cursor.execute("""INSERT INTO HudCache
+ (gametypeId, playerId, activeSeats, position, tourneyTypeId,
+ HDs, street0VPI, street0Aggr, street0_3B4BChance, street0_3B4BDone,
+ street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
+ street1Aggr, street2Aggr, street3Aggr, street4Aggr, otherRaisedStreet1,
+ otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4, foldToOtherRaisedStreet1, foldToOtherRaisedStreet2,
+ foldToOtherRaisedStreet3, foldToOtherRaisedStreet4, wonWhenSeenStreet1, wonAtSD, stealAttemptChance,
+ stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal,
+ street1CBChance, street1CBDone, street2CBChance, street2CBDone, street3CBChance,
+ street3CBDone, street4CBChance, street4CBDone, foldToStreet1CBChance, foldToStreet1CBDone,
+ foldToStreet2CBChance, foldToStreet2CBDone, foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance,
+ foldToStreet4CBDone, totalProfit, street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance,
+ street2CheckCallRaiseDone, street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone)
+ VALUES (%s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s,
+ %s, %s, %s, %s, %s)""", (row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40], row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50], row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60]))
+ else:
+ #print "storing updated hud data line"
+ cursor.execute("""UPDATE HudCache
+ SET HDs=%s, street0VPI=%s, street0Aggr=%s, street0_3B4BChance=%s, street0_3B4BDone=%s,
+ street1Seen=%s, street2Seen=%s, street3Seen=%s, street4Seen=%s, sawShowdown=%s,
+ street1Aggr=%s, street2Aggr=%s, street3Aggr=%s, street4Aggr=%s, otherRaisedStreet1=%s,
+ otherRaisedStreet2=%s, otherRaisedStreet3=%s, otherRaisedStreet4=%s, foldToOtherRaisedStreet1=%s, foldToOtherRaisedStreet2=%s,
+ foldToOtherRaisedStreet3=%s, foldToOtherRaisedStreet4=%s, wonWhenSeenStreet1=%s, wonAtSD=%s, stealAttemptChance=%s,
+ stealAttempted=%s, foldBbToStealChance=%s, foldedBbToSteal=%s, foldSbToStealChance=%s, foldedSbToSteal=%s,
+ street1CBChance=%s, street1CBDone=%s, street2CBChance=%s, street2CBDone=%s, street3CBChance=%s,
+ street3CBDone=%s, street4CBChance=%s, street4CBDone=%s, foldToStreet1CBChance=%s, foldToStreet1CBDone=%s,
+ foldToStreet2CBChance=%s, foldToStreet2CBDone=%s, foldToStreet3CBChance=%s, foldToStreet3CBDone=%s, foldToStreet4CBChance=%s,
+ foldToStreet4CBDone=%s, totalProfit=%s, street1CheckCallRaiseChance=%s, street1CheckCallRaiseDone=%s, street2CheckCallRaiseChance=%s,
+ street2CheckCallRaiseDone=%s, street3CheckCallRaiseChance=%s, street3CheckCallRaiseDone=%s, street4CheckCallRaiseChance=%s, street4CheckCallRaiseDone=%s
+ WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s AND position=%s AND tourneyTypeId=%s""", (row[6], row[7], row[8], row[9], row[10],
+ row[11], row[12], row[13], row[14], row[15],
+ row[16], row[17], row[18], row[19], row[20],
+ row[21], row[22], row[23], row[24], row[25],
+ row[26], row[27], row[28], row[29], row[30],
+ row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40],
+ row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50],
+ row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60],
+ row[1], row[2], row[3], str(row[4]), row[5]))
+# else:
+# print "todo: implement storeHudCache for stud base"
+#end def storeHudCache
+
+def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime):
+ cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId))
+ tmp=cursor.fetchone()
+ #print "tried SELECTing tourneys.id, result:",tmp
+
+ try:
+ len(tmp)
+ except TypeError:#means we have to create new one
+ cursor.execute("""INSERT INTO Tourneys
+ (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)
+ VALUES (%s, %s, %s, %s, %s)""", (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime))
+ cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId))
+ tmp=cursor.fetchone()
+ #print "created new tourneys.id:",tmp
+ return tmp[0]
+#end def store_tourneys
+
+def store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings):
+ result=[]
+ #print "in store_tourneys_players. tourney_id:",tourney_id
+ #print "player_ids:",player_ids
+ #print "payin_amounts:",payin_amounts
+ #print "ranks:",ranks
+ #print "winnings:",winnings
+ for i in range (len(player_ids)):
+ cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", (tourney_id, player_ids[i]))
+ tmp=cursor.fetchone()
+ #print "tried SELECTing tourneys_players.id:",tmp
+
+ try:
+ len(tmp)
+ except TypeError:
+ cursor.execute("""INSERT INTO TourneysPlayers
+ (tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""",
+ (tourney_id, player_ids[i], payin_amounts[i], ranks[i], winnings[i]))
+
+ cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s",
+ (tourney_id, player_ids[i]))
+ tmp=cursor.fetchone()
+ #print "created new tourneys_players.id:",tmp
+ result.append(tmp[0])
+ return result
+#end def store_tourneys_players
diff --git a/regression-test/known-broken/ftp-omaha-hi-pl-ring-001-005.txt b/regression-test/known-broken/ftp-omaha-hi-pl-ring-001-005.txt
index b2b4ebb6..bebd282b 100644
--- a/regression-test/known-broken/ftp-omaha-hi-pl-ring-001-005.txt
+++ b/regression-test/known-broken/ftp-omaha-hi-pl-ring-001-005.txt
@@ -1,271 +1,271 @@
-Full Tilt Poker Game #6929537410: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:15:44 ET - 2008/06/22
-Seat 1: player16 ($94.90)
-Seat 2: player25 ($147)
-Seat 3: player18 ($62.80)
-Seat 4: player19 ($136.55)
-Seat 5: play-er26 ($56.05)
-Seat 6: player21 ($252.95)
-Seat 7: player22 ($200)
-Seat 8: player23 ($162.50)
-Seat 9: player24 ($270.70)
-player24 posts the small blind of $0.50
-player16 posts the big blind of $1
-player22 posts $1
-The button is in seat #8
-*** HOLE CARDS ***
-player25 folds
-player25 stands up
-player18 folds
-player19 folds
-play-er26 folds
-player21 folds
-player22 checks
-player23 calls $1
-player17 adds $100
-player24 calls $0.50
-player16 checks
-*** FLOP *** [4s Kc 8s]
-player24 has 15 seconds left to act
-player24 checks
-player16 checks
-player22 checks
-player23 checks
-*** TURN *** [4s Kc 8s] [6s]
-player24 checks
-player16 checks
-player22 checks
-player23 bets $4
-player24 calls $4
-player16 folds
-player22 folds
-*** RIVER *** [4s Kc 8s 6s] [Qc]
-player24 checks
-player23 checks
-*** SHOW DOWN ***
-player23 shows [Td 5s 3d Js] a flush, Jack high
-player24 mucks
-player23 wins the pot ($11.40) with a flush, Jack high
-*** SUMMARY ***
-Total pot $12 | Rake $0.60
-Board: [4s Kc 8s 6s Qc]
-Seat 1: player16 (big blind) folded on the Turn
-Seat 2: player25 didn't bet (folded)
-Seat 3: player18 didn't bet (folded)
-Seat 4: player19 didn't bet (folded)
-Seat 5: play-er26 didn't bet (folded)
-Seat 6: player21 didn't bet (folded)
-Seat 7: player22 folded on the Turn
-Seat 8: player23 (button) collected ($11.40)
-Seat 9: player24 (small blind) mucked
-
-
-
-Full Tilt Poker Game #6929553738: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:17:06 ET - 2008/06/22
-Seat 1: player16 ($93.90)
-Seat 2: player17 ($100)
-Seat 3: player18 ($62.80)
-Seat 4: player19 ($136.55)
-Seat 5: play-er26 ($56.05)
-Seat 6: player21 ($252.95)
-Seat 7: player22 ($199)
-Seat 8: player23 ($168.90)
-Seat 9: player24 ($265.70)
-player16 posts the small blind of $0.50
-player17 posts the big blind of $1
-The button is in seat #9
-*** HOLE CARDS ***
-player18 folds
-play-er26 stands up
-player19 raises to $2
-play-er26 folds
-player21 calls $2
-player22 has 15 seconds left to act
-player22 folds
-player23 folds
-player24 folds
-player16 calls $1.50
-player17 calls $1
-*** FLOP *** [Jc 4c Kc]
-player16 checks
-player17 checks
-player19 checks
-player21 checks
-*** TURN *** [Jc 4c Kc] [7h]
-player16 checks
-player17 checks
-player19 bets $3.50
-player21 folds
-player16 folds
-player17 calls $3.50
-*** RIVER *** [Jc 4c Kc 7h] [8s]
-player17 checks
-player19 has 15 seconds left to act
-player19 bets $10
-player17 calls $10
-*** SHOW DOWN ***
-player19 shows [4s Tc As Ac] a flush, Ace high
-player17 mucks
-player19 wins the pot ($33.25) with a flush, Ace high
-*** SUMMARY ***
-Total pot $35 | Rake $1.75
-Board: [Jc 4c Kc 7h 8s]
-Seat 1: player16 (small blind) folded on the Turn
-Seat 2: player17 (big blind) mucked
-Seat 3: player18 didn't bet (folded)
-Seat 4: player19 collected ($33.25)
-Seat 5: play-er26 didn't bet (folded)
-Seat 6: player21 folded on the Turn
-Seat 7: player22 didn't bet (folded)
-Seat 8: player23 didn't bet (folded)
-Seat 9: player24 (button) didn't bet (folded)
-
-
-
-Full Tilt Poker Game #6929572212: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:18:40 ET - 2008/06/22
-Seat 1: player16 ($91.90)
-Seat 2: player17 ($84.50)
-Seat 3: player18 ($62.80)
-Seat 4: player19 ($154.30)
-Seat 6: player21 ($250.95)
-Seat 7: player22 ($199)
-Seat 8: player23 ($168.90)
-Seat 9: player24 ($265.70)
-player17 posts the small blind of $0.50
-player18 posts the big blind of $1
-The button is in seat #1
-*** HOLE CARDS ***
-player19 folds
-player21 folds
-player20 adds $50
-player22 folds
-player23 folds
-player24 folds
-player20 is sitting out
-player16 raises to $2
-player17 folds
-player18 folds
-Uncalled bet of $1 returned to player16
-player16 mucks
-player16 wins the pot ($2.50)
-*** SUMMARY ***
-Total pot $2.50 | Rake $0
-Seat 1: player16 (button) collected ($2.50), mucked
-Seat 2: player17 (small blind) folded before the Flop
-Seat 3: player18 (big blind) folded before the Flop
-Seat 4: player19 didn't bet (folded)
-Seat 6: player21 didn't bet (folded)
-Seat 7: player22 didn't bet (folded)
-Seat 8: player23 didn't bet (folded)
-Seat 9: player24 didn't bet (folded)
-
-
-
-Full Tilt Poker Game #6929576743: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:19:03 ET - 2008/06/22
-Seat 1: player16 ($93.40)
-Seat 2: player17 ($84)
-Seat 3: player18 ($61.80)
-Seat 4: player19 ($154.30)
-Seat 5: player20 ($50), is sitting out
-Seat 6: player21 ($250.95)
-Seat 7: player22 ($199)
-Seat 8: player23 ($168.90)
-Seat 9: player24 ($265.70)
-player18 posts the small blind of $0.50
-player19 posts the big blind of $1
-The button is in seat #2
-*** HOLE CARDS ***
-player20 has returned
-player21 calls $1
-player22 folds
-player23 calls $1
-player24 calls $1
-player16 raises to $4
-player17 folds
-player18 folds
-player19 folds
-player21 folds
-player23 folds
-player17 is sitting out
-player24 has 15 seconds left to act
-player24 calls $3
-*** FLOP *** [Tc 9s 7h]
-player24 checks
-player16 has 15 seconds left to act
-player16 bets $8
-player24 folds
-Uncalled bet of $8 returned to player16
-player16 mucks
-player16 wins the pot ($10.95)
-*** SUMMARY ***
-Total pot $11.50 | Rake $0.55
-Board: [Tc 9s 7h]
-Seat 1: player16 collected ($10.95), mucked
-Seat 2: player17 (button) didn't bet (folded)
-Seat 3: player18 (small blind) folded before the Flop
-Seat 4: player19 (big blind) folded before the Flop
-Seat 5: player20 is sitting out
-Seat 6: player21 folded before the Flop
-Seat 7: player22 didn't bet (folded)
-Seat 8: player23 folded before the Flop
-Seat 9: player24 folded on the Flop
-
-
-
-Full Tilt Poker Game #6929587483: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:19:57 ET - 2008/06/22
-Seat 1: player16 ($100.35)
-Seat 2: player17 ($84), is sitting out
-Seat 3: player18 ($61.30)
-Seat 4: player19 ($153.30)
-Seat 5: player20 ($50)
-Seat 6: player21 ($249.95)
-Seat 7: player22 ($199)
-Seat 8: player23 ($167.90)
-Seat 9: player24 ($261.70)
-player19 posts the small blind of $0.50
-player20 posts the big blind of $1
-The button is in seat #3
-*** HOLE CARDS ***
-player21 folds
-player22 folds
-player21 stands up
-player23 calls $1
-player24 calls $1
-player16 folds
-player18 folds
-player19 calls $0.50
-player20 checks
-*** FLOP *** [Jd Td 2c]
-roguern adds $100
-player19 bets $3
-player20 folds
-player23 folds
-player24 has 15 seconds left to act
-player24 raises to $11
-player19 raises to $37
-player24 raises to $115
-player19 raises to $152.30, and is all in
-player24 calls $37.30
-player19 shows [Jc Jh 7s 5h]
-player24 shows [Kh Ad 6h Qd]
-*** TURN *** [Jd Td 2c] [As]
-*** RIVER *** [Jd Td 2c As] [8s]
-player19 shows three of a kind, Jacks
-player24 shows a straight, Ace high
-player24 wins the pot ($305.60) with a straight, Ace high
-player19 is sitting out
-*** SUMMARY ***
-Total pot $308.60 | Rake $3
-Board: [Jd Td 2c As 8s]
-Seat 1: player16 didn't bet (folded)
-Seat 2: player17 is sitting out
-Seat 3: player18 (button) didn't bet (folded)
-Seat 4: player19 (small blind) showed [Jc Jh 7s 5h] and lost with three of a kind, Jacks
-Seat 5: player20 (big blind) folded on the Flop
-Seat 6: player21 didn't bet (folded)
-Seat 7: player22 didn't bet (folded)
-Seat 8: player23 folded on the Flop
-Seat 9: player24 showed [Kh Ad 6h Qd] and won ($305.60) with a straight, Ace high
-
-
-
+Full Tilt Poker Game #6929537410: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:15:44 ET - 2008/06/22
+Seat 1: player16 ($94.90)
+Seat 2: player25 ($147)
+Seat 3: player18 ($62.80)
+Seat 4: player19 ($136.55)
+Seat 5: play-er26 ($56.05)
+Seat 6: player21 ($252.95)
+Seat 7: player22 ($200)
+Seat 8: player23 ($162.50)
+Seat 9: player24 ($270.70)
+player24 posts the small blind of $0.50
+player16 posts the big blind of $1
+player22 posts $1
+The button is in seat #8
+*** HOLE CARDS ***
+player25 folds
+player25 stands up
+player18 folds
+player19 folds
+play-er26 folds
+player21 folds
+player22 checks
+player23 calls $1
+player17 adds $100
+player24 calls $0.50
+player16 checks
+*** FLOP *** [4s Kc 8s]
+player24 has 15 seconds left to act
+player24 checks
+player16 checks
+player22 checks
+player23 checks
+*** TURN *** [4s Kc 8s] [6s]
+player24 checks
+player16 checks
+player22 checks
+player23 bets $4
+player24 calls $4
+player16 folds
+player22 folds
+*** RIVER *** [4s Kc 8s 6s] [Qc]
+player24 checks
+player23 checks
+*** SHOW DOWN ***
+player23 shows [Td 5s 3d Js] a flush, Jack high
+player24 mucks
+player23 wins the pot ($11.40) with a flush, Jack high
+*** SUMMARY ***
+Total pot $12 | Rake $0.60
+Board: [4s Kc 8s 6s Qc]
+Seat 1: player16 (big blind) folded on the Turn
+Seat 2: player25 didn't bet (folded)
+Seat 3: player18 didn't bet (folded)
+Seat 4: player19 didn't bet (folded)
+Seat 5: play-er26 didn't bet (folded)
+Seat 6: player21 didn't bet (folded)
+Seat 7: player22 folded on the Turn
+Seat 8: player23 (button) collected ($11.40)
+Seat 9: player24 (small blind) mucked
+
+
+
+Full Tilt Poker Game #6929553738: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:17:06 ET - 2008/06/22
+Seat 1: player16 ($93.90)
+Seat 2: player17 ($100)
+Seat 3: player18 ($62.80)
+Seat 4: player19 ($136.55)
+Seat 5: play-er26 ($56.05)
+Seat 6: player21 ($252.95)
+Seat 7: player22 ($199)
+Seat 8: player23 ($168.90)
+Seat 9: player24 ($265.70)
+player16 posts the small blind of $0.50
+player17 posts the big blind of $1
+The button is in seat #9
+*** HOLE CARDS ***
+player18 folds
+play-er26 stands up
+player19 raises to $2
+play-er26 folds
+player21 calls $2
+player22 has 15 seconds left to act
+player22 folds
+player23 folds
+player24 folds
+player16 calls $1.50
+player17 calls $1
+*** FLOP *** [Jc 4c Kc]
+player16 checks
+player17 checks
+player19 checks
+player21 checks
+*** TURN *** [Jc 4c Kc] [7h]
+player16 checks
+player17 checks
+player19 bets $3.50
+player21 folds
+player16 folds
+player17 calls $3.50
+*** RIVER *** [Jc 4c Kc 7h] [8s]
+player17 checks
+player19 has 15 seconds left to act
+player19 bets $10
+player17 calls $10
+*** SHOW DOWN ***
+player19 shows [4s Tc As Ac] a flush, Ace high
+player17 mucks
+player19 wins the pot ($33.25) with a flush, Ace high
+*** SUMMARY ***
+Total pot $35 | Rake $1.75
+Board: [Jc 4c Kc 7h 8s]
+Seat 1: player16 (small blind) folded on the Turn
+Seat 2: player17 (big blind) mucked
+Seat 3: player18 didn't bet (folded)
+Seat 4: player19 collected ($33.25)
+Seat 5: play-er26 didn't bet (folded)
+Seat 6: player21 folded on the Turn
+Seat 7: player22 didn't bet (folded)
+Seat 8: player23 didn't bet (folded)
+Seat 9: player24 (button) didn't bet (folded)
+
+
+
+Full Tilt Poker Game #6929572212: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:18:40 ET - 2008/06/22
+Seat 1: player16 ($91.90)
+Seat 2: player17 ($84.50)
+Seat 3: player18 ($62.80)
+Seat 4: player19 ($154.30)
+Seat 6: player21 ($250.95)
+Seat 7: player22 ($199)
+Seat 8: player23 ($168.90)
+Seat 9: player24 ($265.70)
+player17 posts the small blind of $0.50
+player18 posts the big blind of $1
+The button is in seat #1
+*** HOLE CARDS ***
+player19 folds
+player21 folds
+player20 adds $50
+player22 folds
+player23 folds
+player24 folds
+player20 is sitting out
+player16 raises to $2
+player17 folds
+player18 folds
+Uncalled bet of $1 returned to player16
+player16 mucks
+player16 wins the pot ($2.50)
+*** SUMMARY ***
+Total pot $2.50 | Rake $0
+Seat 1: player16 (button) collected ($2.50), mucked
+Seat 2: player17 (small blind) folded before the Flop
+Seat 3: player18 (big blind) folded before the Flop
+Seat 4: player19 didn't bet (folded)
+Seat 6: player21 didn't bet (folded)
+Seat 7: player22 didn't bet (folded)
+Seat 8: player23 didn't bet (folded)
+Seat 9: player24 didn't bet (folded)
+
+
+
+Full Tilt Poker Game #6929576743: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:19:03 ET - 2008/06/22
+Seat 1: player16 ($93.40)
+Seat 2: player17 ($84)
+Seat 3: player18 ($61.80)
+Seat 4: player19 ($154.30)
+Seat 5: player20 ($50), is sitting out
+Seat 6: player21 ($250.95)
+Seat 7: player22 ($199)
+Seat 8: player23 ($168.90)
+Seat 9: player24 ($265.70)
+player18 posts the small blind of $0.50
+player19 posts the big blind of $1
+The button is in seat #2
+*** HOLE CARDS ***
+player20 has returned
+player21 calls $1
+player22 folds
+player23 calls $1
+player24 calls $1
+player16 raises to $4
+player17 folds
+player18 folds
+player19 folds
+player21 folds
+player23 folds
+player17 is sitting out
+player24 has 15 seconds left to act
+player24 calls $3
+*** FLOP *** [Tc 9s 7h]
+player24 checks
+player16 has 15 seconds left to act
+player16 bets $8
+player24 folds
+Uncalled bet of $8 returned to player16
+player16 mucks
+player16 wins the pot ($10.95)
+*** SUMMARY ***
+Total pot $11.50 | Rake $0.55
+Board: [Tc 9s 7h]
+Seat 1: player16 collected ($10.95), mucked
+Seat 2: player17 (button) didn't bet (folded)
+Seat 3: player18 (small blind) folded before the Flop
+Seat 4: player19 (big blind) folded before the Flop
+Seat 5: player20 is sitting out
+Seat 6: player21 folded before the Flop
+Seat 7: player22 didn't bet (folded)
+Seat 8: player23 folded before the Flop
+Seat 9: player24 folded on the Flop
+
+
+
+Full Tilt Poker Game #6929587483: Table Green (deep) - $0.50/$1 - Pot Limit Omaha Hi - 17:19:57 ET - 2008/06/22
+Seat 1: player16 ($100.35)
+Seat 2: player17 ($84), is sitting out
+Seat 3: player18 ($61.30)
+Seat 4: player19 ($153.30)
+Seat 5: player20 ($50)
+Seat 6: player21 ($249.95)
+Seat 7: player22 ($199)
+Seat 8: player23 ($167.90)
+Seat 9: player24 ($261.70)
+player19 posts the small blind of $0.50
+player20 posts the big blind of $1
+The button is in seat #3
+*** HOLE CARDS ***
+player21 folds
+player22 folds
+player21 stands up
+player23 calls $1
+player24 calls $1
+player16 folds
+player18 folds
+player19 calls $0.50
+player20 checks
+*** FLOP *** [Jd Td 2c]
+roguern adds $100
+player19 bets $3
+player20 folds
+player23 folds
+player24 has 15 seconds left to act
+player24 raises to $11
+player19 raises to $37
+player24 raises to $115
+player19 raises to $152.30, and is all in
+player24 calls $37.30
+player19 shows [Jc Jh 7s 5h]
+player24 shows [Kh Ad 6h Qd]
+*** TURN *** [Jd Td 2c] [As]
+*** RIVER *** [Jd Td 2c As] [8s]
+player19 shows three of a kind, Jacks
+player24 shows a straight, Ace high
+player24 wins the pot ($305.60) with a straight, Ace high
+player19 is sitting out
+*** SUMMARY ***
+Total pot $308.60 | Rake $3
+Board: [Jd Td 2c As 8s]
+Seat 1: player16 didn't bet (folded)
+Seat 2: player17 is sitting out
+Seat 3: player18 (button) didn't bet (folded)
+Seat 4: player19 (small blind) showed [Jc Jh 7s 5h] and lost with three of a kind, Jacks
+Seat 5: player20 (big blind) folded on the Flop
+Seat 6: player21 didn't bet (folded)
+Seat 7: player22 didn't bet (folded)
+Seat 8: player23 folded on the Flop
+Seat 9: player24 showed [Kh Ad 6h Qd] and won ($305.60) with a straight, Ace high
+
+
+
diff --git a/regression-test/known-broken/ftp-stud-hilo-ring-001.txt b/regression-test/known-broken/ftp-stud-hilo-ring-001.txt
index e23cb335..956d1d14 100644
--- a/regression-test/known-broken/ftp-stud-hilo-ring-001.txt
+++ b/regression-test/known-broken/ftp-stud-hilo-ring-001.txt
@@ -1,61 +1,61 @@
-Full Tilt Poker Game #6367428246: Table Mountain Mesa - $15/$30 Ante $3 - Limit Stud H/L - 23:47:38 ET - 2008/05/10
-Seat 1: Player_8 ($446), is sitting out
-Seat 2: Play er9 ($303.50)
-Seat 3: P layer10 ($613), is sitting out
-Seat 4: Player_11 ($164)
-Seat 5: Player1 2 ($543.50), is sitting out
-Seat 6: Player13 ($912.50)
-Seat 7: Player14 ($430), is sitting out
-Seat 8: Player15 ($531.50)
-Player15 antes $3
-Player_11 antes $3
-Player13 antes $3
-Play er9 antes $3
-*** 3RD STREET ***
-Dealt to Play er9 [2s]
-Dealt to Player_11 [3c]
-Dealt to Player13 [8c]
-Dealt to Player15 [Jc]
-Play er9 is low with [2s]
-Play er9 brings in for $5
-Player_11 folds
-Player13 completes it to $15
-Player15 folds
-Play er9 calls $10
-*** 4TH STREET ***
-Dealt to Play er9 [2s] [6c]
-Dealt to Player13 [8c] [5h]
-Player13 bets $15
-Play er9 calls $15
-*** 5TH STREET ***
-Dealt to Play er9 [2s 6c] [Ac]
-Dealt to Player13 [8c 5h] [Ah]
-Player13 bets $30
-Play er9 calls $30
-*** 6TH STREET ***
-Dealt to Play er9 [2s 6c Ac] [2c]
-Dealt to Player13 [8c 5h Ah] [Jd]
-Play er9 bets $30
-Player13 calls $30
-*** 7TH STREET ***
-Play er9 bets $30
-Player13 calls $30
-*** SHOW DOWN ***
-Play er9 shows [5c 4h 2s 6c Ac 2c 2h] three of a kind, Twos, for high and 6,5,4,2,A, for low
-Player13 mucks
-Play er9 wins the high pot ($125) with three of a kind, Twos
-Play er9 wins the low pot ($125) with 6,5,4,2,A
-*** SUMMARY ***
-Total pot $252 | Rake $2
-Seat 1: Player_8 is sitting out
-Seat 2: Play er9 collected ($250)
-Seat 3: P layer10 is sitting out
-Seat 4: Player_11 folded on 3rd St.
-Seat 5: Player1 2 is sitting out
-Seat 6: Player13 mucked
-Seat 7: Player14 is sitting out
-Seat 8: Player15 folded on 3rd St.
-
-
-
+Full Tilt Poker Game #6367428246: Table Mountain Mesa - $15/$30 Ante $3 - Limit Stud H/L - 23:47:38 ET - 2008/05/10
+Seat 1: Player_8 ($446), is sitting out
+Seat 2: Play er9 ($303.50)
+Seat 3: P layer10 ($613), is sitting out
+Seat 4: Player_11 ($164)
+Seat 5: Player1 2 ($543.50), is sitting out
+Seat 6: Player13 ($912.50)
+Seat 7: Player14 ($430), is sitting out
+Seat 8: Player15 ($531.50)
+Player15 antes $3
+Player_11 antes $3
+Player13 antes $3
+Play er9 antes $3
+*** 3RD STREET ***
+Dealt to Play er9 [2s]
+Dealt to Player_11 [3c]
+Dealt to Player13 [8c]
+Dealt to Player15 [Jc]
+Play er9 is low with [2s]
+Play er9 brings in for $5
+Player_11 folds
+Player13 completes it to $15
+Player15 folds
+Play er9 calls $10
+*** 4TH STREET ***
+Dealt to Play er9 [2s] [6c]
+Dealt to Player13 [8c] [5h]
+Player13 bets $15
+Play er9 calls $15
+*** 5TH STREET ***
+Dealt to Play er9 [2s 6c] [Ac]
+Dealt to Player13 [8c 5h] [Ah]
+Player13 bets $30
+Play er9 calls $30
+*** 6TH STREET ***
+Dealt to Play er9 [2s 6c Ac] [2c]
+Dealt to Player13 [8c 5h Ah] [Jd]
+Play er9 bets $30
+Player13 calls $30
+*** 7TH STREET ***
+Play er9 bets $30
+Player13 calls $30
+*** SHOW DOWN ***
+Play er9 shows [5c 4h 2s 6c Ac 2c 2h] three of a kind, Twos, for high and 6,5,4,2,A, for low
+Player13 mucks
+Play er9 wins the high pot ($125) with three of a kind, Twos
+Play er9 wins the low pot ($125) with 6,5,4,2,A
+*** SUMMARY ***
+Total pot $252 | Rake $2
+Seat 1: Player_8 is sitting out
+Seat 2: Play er9 collected ($250)
+Seat 3: P layer10 is sitting out
+Seat 4: Player_11 folded on 3rd St.
+Seat 5: Player1 2 is sitting out
+Seat 6: Player13 mucked
+Seat 7: Player14 is sitting out
+Seat 8: Player15 folded on 3rd St.
+
+
+
diff --git a/website/img/Screenshot-Graph-v137.png b/website/img/Screenshot-Graph-v137.png
index ae281d71..5faaf758 100644
Binary files a/website/img/Screenshot-Graph-v137.png and b/website/img/Screenshot-Graph-v137.png differ