diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py
index 1f5a933e..cd63e871 100644
--- a/pyfpdb/DerivedStats.py
+++ b/pyfpdb/DerivedStats.py
@@ -243,44 +243,34 @@ class DerivedStats():
first betting round is 0
NOTE: HU, both values are negative for non-stud games
NOTE2: I've never seen a HU stud match"""
- # The position calculation must be done differently for Stud and other games as
- # Stud the 'blind' acts first - in all other games they act last.
- #
- #This function is going to get it wrong when there in situations where there
- # is no small blind. I can live with that.
actions = hand.actions[hand.holeStreets[0]]
# Note: pfbao list may not include big blind if all others folded
players = self.pfbao(actions)
+ # set blinds first, then others from pfbao list, avoids problem if bb
+ # is missing from pfbao list or if there is no small blind
+ sb, bb, bi = False, False, False
if hand.gametype['base'] == 'stud':
- positions = [7, 6, 5, 4, 3, 2, 1, 0, 'S', 'B']
- seats = len(players)
- map = []
- # Could posibly change this to be either -2 or -1 depending if they complete or bring-in
- # First player to act is -1, last player is 0 for 6 players it should look like:
- # ['S', 4, 3, 2, 1, 0]
- map = positions[-seats-1:-1] # Copy required positions from postions array anding in -1
- map = map[-1:] + map[0:-1] # and move the -1 to the start of that array
-
- for i, player in enumerate(players):
- #print "player %s in posn %s" % (player, str(map[i]))
- self.handsplayers[player]['position'] = map[i]
+ # Stud position is determined after cards are dealt
+ bi = [x[0] for x in hand.actions[hand.actionStreets[1]] if x[1] == 'bringin']
else:
- # set blinds first, then others from pfbao list, avoids problem if bb
- # is missing from pfbao list or if there is no small blind
bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'big blind']
sb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'small blind']
- # if there are > 1 sb or bb only the first is used!
- if bb:
- self.handsplayers[bb[0]]['position'] = 'B'
- if bb[0] in players: players.remove(bb[0])
- if sb:
- self.handsplayers[sb[0]]['position'] = 'S'
- if sb[0] in players: players.remove(sb[0])
- #print "bb =", bb, "sb =", sb, "players =", players
- for i,player in enumerate(reversed(players)):
- self.handsplayers[player]['position'] = i
+ # if there are > 1 sb or bb only the first is used!
+ if bb:
+ self.handsplayers[bb[0]]['position'] = 'B'
+ if bb[0] in players: players.remove(bb[0])
+ if sb:
+ self.handsplayers[sb[0]]['position'] = 'S'
+ if sb[0] in players: players.remove(sb[0])
+ if bi:
+ self.handsplayers[bi[0]]['position'] = 'S'
+ if bi[0] in players: players.remove(bi[0])
+
+ #print "DEBUG: bb: '%s' sb: '%s' bi: '%s' plyrs: '%s'" %(bb, sb, bi, players)
+ for i,player in enumerate(reversed(players)):
+ self.handsplayers[player]['position'] = i
def assembleHudCache(self, hand):
# No real work to be done - HandsPlayers data already contains the correct info
@@ -289,7 +279,7 @@ class DerivedStats():
def vpip(self, hand):
vpipers = set()
for act in hand.actions[hand.actionStreets[1]]:
- if act[1] in ('calls','bets', 'raises'):
+ if act[1] in ('calls','bets', 'raises', 'completes'):
vpipers.add(act[0])
self.hands['playersVpi'] = len(vpipers)
@@ -387,9 +377,9 @@ class DerivedStats():
if steal_attempt and act != 'folds':
break
- if not steal_attempt and not raised: # if posn in steal_positions and not steal_attempt:
+ if not steal_attempt and not raised and not act in ('bringin'):
self.handsplayers[pname]['raiseFirstInChance'] = True
- if act in ('bets', 'raises'):
+ if act in ('bets', 'raises', 'completes'):
self.handsplayers[pname]['raisedFirstIn'] = True
raised = True
if posn in steal_positions:
@@ -397,7 +387,7 @@ class DerivedStats():
if act == 'calls':
break
- if posn not in steal_positions and act != 'folds':
+ if posn not in steal_positions and act not in ('folds', 'bringin'):
break
def calc34BetStreet0(self, hand):
diff --git a/pyfpdb/Exceptions.py b/pyfpdb/Exceptions.py
index 34b3bade..589f65cc 100644
--- a/pyfpdb/Exceptions.py
+++ b/pyfpdb/Exceptions.py
@@ -73,3 +73,6 @@ class FpdbHandDuplicate(FpdbHandError):
class FpdbHandPartial(FpdbHandError):
pass
+
+class FpdbEndOfFile(FpdbHandError):
+ pass
diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py
index a4660eb2..1b07a67c 100644
--- a/pyfpdb/Filters.py
+++ b/pyfpdb/Filters.py
@@ -1014,6 +1014,25 @@ class Filters(threading.Thread):
vb = gtk.VBox()
cal = gtk.Calendar()
+
+ if entry == self.start_date:
+ cal_date = self.start_date.get_text()
+ if cal_date == '':
+ self.cursor.execute(self.sql.query['get_first_date'])
+ result = self.db.cursor.fetchall()
+ cal_date = result[0][0].split()[0]
+ self.start_date.set_text(cal_date)
+ elif entry == self.end_date:
+ cal_date = self.end_date.get_text()
+ if cal_date == '':
+ self.cursor.execute(self.sql.query['get_last_date'])
+ result = self.db.cursor.fetchall()
+ cal_date = result[0][0].split()[0]
+ self.end_date.set_text(cal_date)
+
+ (year,month,day)=cal_date.split('-')
+ cal.select_month(int(month)-1, int(year))
+ cal.select_day(int(day))
vb.pack_start(cal, expand=False, padding=0)
btn = gtk.Button(_('Done'))
@@ -1039,9 +1058,16 @@ class Filters(threading.Thread):
t2 = self.end_date.get_text()
if t1 == '':
- t1 = '1970-01-02'
+ self.cursor.execute(self.sql.query['get_first_date'])
+ result = self.db.cursor.fetchall()
+ t1 = result[0][0].split()[0]
+ self.start_date.set_text(t1)
+
if t2 == '':
- t2 = '2020-12-12'
+ self.cursor.execute(self.sql.query['get_last_date'])
+ result = self.db.cursor.fetchall()
+ t2 = result[0][0].split()[0]
+ self.end_date.set_text(t2)
s1 = strptime(t1, "%Y-%m-%d") # make time_struct
s2 = strptime(t2, "%Y-%m-%d")
diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py
index d601e0d2..0d40fd59 100644
--- a/pyfpdb/GuiRingPlayerStats.py
+++ b/pyfpdb/GuiRingPlayerStats.py
@@ -85,9 +85,9 @@ class DemoTips(TreeViewTooltips):
def get_tooltip(self, view, column, path):
model = view.get_model()
cards = model[path][0]
-
title=column.get_title()
- display='%s\n%s' % (title,onlinehelp[title])
+ if (title == 'Hand' or title == 'Game'): display='' #no tooltips on headers
+ else: display='%s for %s\n%s' % (title,cards,onlinehelp[title])
return (display)
def location(self, x, y, w, h):
diff --git a/pyfpdb/HUD_config.test.xml b/pyfpdb/HUD_config.test.xml
index 6df2a4ea..77f27924 100644
--- a/pyfpdb/HUD_config.test.xml
+++ b/pyfpdb/HUD_config.test.xml
@@ -574,7 +574,6 @@ Left-Drag to Move"
-
diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example
index f650f52b..9afad9dc 100644
--- a/pyfpdb/HUD_config.xml.example
+++ b/pyfpdb/HUD_config.xml.example
@@ -772,7 +772,6 @@ Left-Drag to Move"
-
diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py
index 0909d42d..ce0e4e7a 100644
--- a/pyfpdb/Hand.py
+++ b/pyfpdb/Hand.py
@@ -281,9 +281,143 @@ db: a connected Database object"""
def select(self, handId):
""" Function to create Hand object from database """
+ c = cnxn.cursor()
+
+ # We need at least sitename, gametype, handid
+ # for the Hand.__init__
+ c.execute("""SELECT
+ s.name,
+ g.category,
+ g.base,
+ g.type,
+ g.limitType,
+ g.hilo,
+ round(g.smallBlind / 100.0,2),
+ round(g.bigBlind / 100.0,2),
+ round(g.smallBet / 100.0,2),
+ round(g.bigBet / 100.0,2),
+ s.currency,
+ h.boardcard1,
+ h.boardcard2,
+ h.boardcard3,
+ h.boardcard4,
+ h.boardcard5
+ FROM
+ hands as h,
+ sites as s,
+ gametypes as g,
+ handsplayers as hp,
+ players as p
+ WHERE
+ h.id = %(handid)s
+ and g.id = h.gametypeid
+ and hp.handid = h.id
+ and p.id = hp.playerid
+ and s.id = p.siteid
+ limit 1""", {'handid':handid})
+ #TODO: siteid should be in hands table - we took the scenic route through players here.
+ res = c.fetchone()
+ gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
+ c = Configuration.Config()
+ h = HoldemOmahaHand(config = c, hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
+ cards = map(Card.valueSuitFromCard, res[11:16] )
+ if cards[0]:
+ h.setCommunityCards('FLOP', cards[0:3])
+ if cards[3]:
+ h.setCommunityCards('TURN', [cards[3]])
+ if cards[4]:
+ h.setCommunityCards('RIVER', [cards[4]])
+ #[Card.valueSuitFromCard(x) for x in cards]
+
+ # HandInfo : HID, TABLE
+ # BUTTON - why is this treated specially in Hand?
+ # answer: it is written out in hand histories
+ # still, I think we should record all the active seat positions in a seat_order array
+ c.execute("""SELECT
+ h.sitehandno as hid,
+ h.tablename as table,
+ h.startTime as startTime
+ FROM
+ hands as h
+ WHERE h.id = %(handid)s
+ """, {'handid':handid})
+ res = c.fetchone()
+ h.handid = res[0]
+ h.tablename = res[1]
+ h.startTime = res[2] # automatically a datetime
+
+ # PlayerStacks
+ c.execute("""SELECT
+ hp.seatno,
+ round(hp.winnings / 100.0,2) as winnings,
+ p.name,
+ round(hp.startcash / 100.0,2) as chips,
+ hp.card1,hp.card2,
+ hp.position
+ FROM
+ handsplayers as hp,
+ players as p
+ WHERE
+ hp.handid = %(handid)s
+ and p.id = hp.playerid
+ """, {'handid':handid})
+ for (seat, winnings, name, chips, card1,card2, position) in c.fetchall():
+ h.addPlayer(seat,name,chips)
+ if card1 and card2:
+ h.addHoleCards(map(Card.valueSuitFromCard, (card1,card2)), name, dealt=True)
+ if winnings > 0:
+ h.addCollectPot(name, winnings)
+ if position == 'B':
+ h.buttonpos = seat
+ # actions
+ c.execute("""SELECT
+ (ha.street,ha.actionno) as actnum,
+ p.name,
+ ha.street,
+ ha.action,
+ ha.allin,
+ round(ha.amount / 100.0,2)
+ FROM
+ handsplayers as hp,
+ handsactions as ha,
+ players as p
+ WHERE
+ hp.handid = %(handid)s
+ and ha.handsplayerid = hp.id
+ and p.id = hp.playerid
+ ORDER BY
+ ha.street,ha.actionno
+ """, {'handid':handid})
+ res = c.fetchall()
+ for (actnum,player, streetnum, act, allin, amount) in res:
+ act=act.strip()
+ street = h.allStreets[streetnum+1]
+ if act==u'blind':
+ h.addBlind(player, 'big blind', amount)
+ # TODO: The type of blind is not recorded in the DB.
+ # TODO: preflop street name anomalies in Hand
+ elif act==u'fold':
+ h.addFold(street,player)
+ elif act==u'call':
+ h.addCall(street,player,amount)
+ elif act==u'bet':
+ h.addBet(street,player,amount)
+ elif act==u'check':
+ h.addCheck(street,player)
+ elif act==u'unbet':
+ pass
+ else:
+ print act, player, streetnum, allin, amount
+ # TODO : other actions
+ #hhc.readShowdownActions(self)
+ #hc.readShownCards(self)
+ h.totalPot()
+ h.rake = h.totalpot - h.totalcollected
+
+ return h
def addPlayer(self, seat, name, chips):
"""\
@@ -1540,146 +1674,4 @@ class Pot(object):
return ret + ''.join([ (" Side pot %s%.2f." % (self.sym, self.pots[x]) ) for x in xrange(1, len(self.pots)) ])
-def assemble(cnxn, handid):
- c = cnxn.cursor()
- # We need at least sitename, gametype, handid
- # for the Hand.__init__
- c.execute("""
-select
- s.name,
- g.category,
- g.base,
- g.type,
- g.limitType,
- g.hilo,
- round(g.smallBlind / 100.0,2),
- round(g.bigBlind / 100.0,2),
- round(g.smallBet / 100.0,2),
- round(g.bigBet / 100.0,2),
- s.currency,
- h.boardcard1,
- h.boardcard2,
- h.boardcard3,
- h.boardcard4,
- h.boardcard5
-from
- hands as h,
- sites as s,
- gametypes as g,
- handsplayers as hp,
- players as p
-where
- h.id = %(handid)s
-and g.id = h.gametypeid
-and hp.handid = h.id
-and p.id = hp.playerid
-and s.id = p.siteid
-limit 1""", {'handid':handid})
- #TODO: siteid should be in hands table - we took the scenic route through players here.
- res = c.fetchone()
- gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
- c = Configuration.Config()
- h = HoldemOmahaHand(config = c, hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
- cards = map(Card.valueSuitFromCard, res[11:16] )
- if cards[0]:
- h.setCommunityCards('FLOP', cards[0:3])
- if cards[3]:
- h.setCommunityCards('TURN', [cards[3]])
- if cards[4]:
- h.setCommunityCards('RIVER', [cards[4]])
- #[Card.valueSuitFromCard(x) for x in cards]
-
- # HandInfo : HID, TABLE
- # BUTTON - why is this treated specially in Hand?
- # answer: it is written out in hand histories
- # still, I think we should record all the active seat positions in a seat_order array
- c.execute("""
-SELECT
- h.sitehandno as hid,
- h.tablename as table,
- h.startTime as startTime
-FROM
- hands as h
-WHERE h.id = %(handid)s
-""", {'handid':handid})
- res = c.fetchone()
- h.handid = res[0]
- h.tablename = res[1]
- h.startTime = res[2] # automatically a datetime
-
- # PlayerStacks
- c.execute("""
-SELECT
- hp.seatno,
- round(hp.winnings / 100.0,2) as winnings,
- p.name,
- round(hp.startcash / 100.0,2) as chips,
- hp.card1,hp.card2,
- hp.position
-FROM
- handsplayers as hp,
- players as p
-WHERE
- hp.handid = %(handid)s
-and p.id = hp.playerid
-""", {'handid':handid})
- for (seat, winnings, name, chips, card1,card2, position) in c.fetchall():
- h.addPlayer(seat,name,chips)
- if card1 and card2:
- h.addHoleCards(map(Card.valueSuitFromCard, (card1,card2)), name, dealt=True)
- if winnings > 0:
- h.addCollectPot(name, winnings)
- if position == 'B':
- h.buttonpos = seat
-
-
- # actions
- c.execute("""
-SELECT
- (ha.street,ha.actionno) as actnum,
- p.name,
- ha.street,
- ha.action,
- ha.allin,
- round(ha.amount / 100.0,2)
-FROM
- handsplayers as hp,
- handsactions as ha,
- players as p
-WHERE
- hp.handid = %(handid)s
-and ha.handsplayerid = hp.id
-and p.id = hp.playerid
-ORDER BY
- ha.street,ha.actionno
-""", {'handid':handid})
- res = c.fetchall()
- for (actnum,player, streetnum, act, allin, amount) in res:
- act=act.strip()
- street = h.allStreets[streetnum+1]
- if act==u'blind':
- h.addBlind(player, 'big blind', amount)
- # TODO: The type of blind is not recorded in the DB.
- # TODO: preflop street name anomalies in Hand
- elif act==u'fold':
- h.addFold(street,player)
- elif act==u'call':
- h.addCall(street,player,amount)
- elif act==u'bet':
- h.addBet(street,player,amount)
- elif act==u'check':
- h.addCheck(street,player)
- elif act==u'unbet':
- pass
- else:
- print act, player, streetnum, allin, amount
- # TODO : other actions
-
- #hhc.readShowdownActions(self)
- #hc.readShownCards(self)
- h.totalPot()
- h.rake = h.totalpot - h.totalcollected
-
-
- return h
diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py
index 15a9e6de..56d184d5 100644
--- a/pyfpdb/Hud.py
+++ b/pyfpdb/Hud.py
@@ -393,7 +393,7 @@ class Hud:
if self.hud_params['h_agg_bb_mult'] != num \
and getattr(self, 'h_aggBBmultItem'+str(num)).get_active():
- log.debug('set_player_aggregation', num)
+ log.debug('set_player_aggregation %d', num)
self.hud_params['h_agg_bb_mult'] = num
for mult in ('1', '2', '3', '10', '10000'):
if mult != str(num):
@@ -404,7 +404,7 @@ class Hud:
if self.hud_params['agg_bb_mult'] != num \
and getattr(self, 'aggBBmultItem'+str(num)).get_active():
- log.debug('set_opponent_aggregation', num)
+ log.debug('set_opponent_aggregation %d', num)
self.hud_params['agg_bb_mult'] = num
for mult in ('1', '2', '3', '10', '10000'):
if mult != str(num):
diff --git a/pyfpdb/IdentifySite.py b/pyfpdb/IdentifySite.py
new file mode 100644
index 00000000..b4f1a8a7
--- /dev/null
+++ b/pyfpdb/IdentifySite.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+#Copyright 2010 Chaz Littlejohn
+#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.
+
+import L10n
+_ = L10n.get_translation()
+
+import re
+import sys
+import os
+import os.path
+from optparse import OptionParser
+import codecs
+import Configuration
+import Database
+
+__ARCHIVE_PRE_HEADER_REGEX='^Hand #(\d+)\s*$|\*{20}\s#\s\d+\s\*+\s+'
+re_SplitArchive = re.compile(__ARCHIVE_PRE_HEADER_REGEX)
+
+
+class IdentifySite:
+ def __init__(self, config, in_path = '-'):
+ self.in_path = in_path
+ self.config = config
+ self.db = Database.Database(config)
+ self.sitelist = {}
+ self.filelist = {}
+ self.generateSiteList()
+ self.walkDirectory(self.in_path, self.sitelist)
+
+ def generateSiteList(self):
+ """Generates a ordered dictionary of site, filter and filter name for each site in hhcs"""
+ for site, hhc in self.config.hhcs.iteritems():
+ filter = hhc.converter
+ filter_name = filter.replace("ToFpdb", "")
+ result = self.db.get_site_id(site)
+ if len(result) == 1:
+ self.sitelist[result[0][0]] = (site, filter, filter_name)
+ else:
+ pass
+
+ def walkDirectory(self, dir, sitelist):
+ """Walks a directory, and executes a callback on each file"""
+ dir = os.path.abspath(dir)
+ for file in [file for file in os.listdir(dir) if not file in [".",".."]]:
+ nfile = os.path.join(dir,file)
+ if os.path.isdir(nfile):
+ self.walkDirectory(nfile, sitelist)
+ else:
+ self.idSite(nfile, sitelist)
+
+ def __listof(self, x):
+ if isinstance(x, list) or isinstance(x, tuple):
+ return x
+ else:
+ return [x]
+
+ def idSite(self, file, sitelist):
+ """Identifies the site the hh file originated from"""
+ if file.endswith('.txt'):
+ self.filelist[file] = ''
+ archive = False
+ for site, info in sitelist.iteritems():
+ mod = __import__(info[1])
+ obj = getattr(mod, info[2], None)
+
+ for kodec in self.__listof(obj.codepage):
+ try:
+ in_fh = codecs.open(file, 'r', kodec)
+ whole_file = in_fh.read()
+ in_fh.close()
+
+ if info[2] in ('OnGame', 'Winamax'):
+ m = obj.re_HandInfo.search(whole_file)
+ elif info[2] in ('PartyPoker'):
+ m = obj.re_GameInfoRing.search(whole_file)
+ if not m:
+ m = obj.re_GameInfoTrny.search(whole_file)
+ else:
+ m = obj.re_GameInfo.search(whole_file)
+ if re_SplitArchive.search(whole_file):
+ archive = True
+ if m:
+ self.filelist[file] = [info[0]] + [info[1]] + [kodec] + [archive]
+ break
+ except:
+ pass
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv[1:]
+
+ config = Configuration.Config(file = "HUD_config.test.xml")
+ in_path = 'regression-test-files/'
+ IdSite = IdentifySite(config, in_path)
+
+ print "\n----------- SITE LIST -----------"
+ for site, info in IdSite.sitelist.iteritems():
+ print site, info
+ print "----------- END SITE LIST -----------"
+
+ print "\n----------- ID REGRESSION FILES -----------"
+ for file, site in IdSite.filelist.iteritems():
+ print file, site
+ print "----------- END ID REGRESSION FILES -----------"
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/pyfpdb/Options.py b/pyfpdb/Options.py
index 98034b50..4646cb6e 100644
--- a/pyfpdb/Options.py
+++ b/pyfpdb/Options.py
@@ -53,6 +53,15 @@ def fpdb_options():
help = _("Print version information and exit."))
parser.add_option("-u", "--usage", action="store_true", dest="usage", default=False,
help=_("Print some useful one liners"))
+ # The following options are used for SplitHandHistory.py
+ parser.add_option("-f", "--file", dest="filename", metavar="FILE", default=None,
+ help=_("Input file in quiet mode"))
+ parser.add_option("-o", "--outpath", dest="outpath", metavar="FILE", default=None,
+ help=_("Input out path in quiet mode"))
+ parser.add_option("-a", "--archive", action="store_true", dest="archive", default=False,
+ help=_("File to be split is a PokerStars or Full Tilt Poker archive file"))
+ parser.add_option("-n", "--numhands", dest="hands", default="100", type="int",
+ help=_("How many hands do you want saved to each file. Default is 100"))
(options, argv) = parser.parse_args()
diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py
index 8b35c525..24719de5 100644
--- a/pyfpdb/PokerStarsToFpdb.py
+++ b/pyfpdb/PokerStarsToFpdb.py
@@ -39,7 +39,7 @@ class PokerStars(HandHistoryConverter):
siteId = 2 # Needs to match id entry in Sites database
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games
- sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\xe2\x82\xac", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE
+ sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\xe2\x82\xac", "GBP": "\xa3", "play": ""} # ADD Euro, Sterling, etc HERE
substitutions = {
'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes
'LS' : "\$|\xe2\x82\xac|" # legal currency symbols - Euro(cp1252, utf-8)
diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py
index eef9ce04..d45ccd38 100644
--- a/pyfpdb/SQL.py
+++ b/pyfpdb/SQL.py
@@ -1396,6 +1396,10 @@ class Sql:
, maxSeats, knockout, rebuy, addOn, speed, shootout, matrix, sng)"""
self.query['get_last_hand'] = "select max(id) from Hands"
+
+ self.query['get_last_date'] = "SELECT MAX(startTime) FROM Hands"
+
+ self.query['get_first_date'] = "SELECT MIN(startTime) FROM Hands"
self.query['get_player_id'] = """
select Players.id AS player_id
diff --git a/pyfpdb/SplitHandHistory.py b/pyfpdb/SplitHandHistory.py
new file mode 100644
index 00000000..52c1d340
--- /dev/null
+++ b/pyfpdb/SplitHandHistory.py
@@ -0,0 +1,207 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+#Copyright 2010 Chaz Littlejohn
+#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.
+
+import L10n
+_ = L10n.get_translation()
+
+# This code is based heavily on stars-support-hh-split.py by Mika Boström
+
+import os
+import sys
+import re
+import codecs
+import Options
+import Configuration
+from Exceptions import *
+from cStringIO import StringIO
+
+(options, argv) = Options.fpdb_options()
+
+__ARCHIVE_PRE_HEADER_REGEX='^Hand #(\d+)\s*$|\*{20}\s#\s\d+\s\*+\s+'
+re_SplitArchive = re.compile(__ARCHIVE_PRE_HEADER_REGEX)
+codepage = ["utf-16", "utf-8", "cp1252"]
+
+
+class SplitHandHistory:
+ def __init__(self, config, in_path = '-', out_path = None, hands = 100, filter = "PokerStarsToFpdb", archive = False):
+ self.config = config
+ self.in_path = in_path
+ self.out_path = out_path
+ if not self.out_path:
+ self.out_path = os.path.dirname(self.in_path)
+ self.hands = hands
+ self.archive = archive
+ self.re_SplitHands = None
+ self.line_delimiter = None
+ self.line_addendum = None
+ self.filedone = False
+
+ #Acquire re_SplitHands for this hh
+ filter_name = filter.replace("ToFpdb", "")
+ mod = __import__(filter)
+ obj = getattr(mod, filter_name, None)
+ self.re_SplitHands = obj.re_SplitHands
+
+ #Determine line delimiter type if any
+ if self.re_SplitHands.match('\n\n'):
+ self.line_delimiter = '\n\n'
+ if self.re_SplitHands.match('\n\n\n'):
+ self.line_delimiter = '\n\n\n'
+
+ #Add new line addendum for sites which match SplitHand to next line as well
+ if filter_name == 'OnGame':
+ self.line_addendum = '*'
+ if filter_name == 'Carbon':
+ self.line_addendum = '= self.hands:
+ break
+ outfile.close()
+
+ def new_file(self, fileno=-1):
+ if fileno < 1:
+ print _('Nope, will not work (fileno=%d)' % fileno)
+ sys.exit(2)
+ basename = os.path.splitext(os.path.basename(self.in_path))[0]
+ name = os.path.join(self.out_path, basename+'-%06d.txt' % fileno)
+ print '-> %s' % name
+ newfile = file(name, 'w')
+ return newfile
+
+ #Archive Hand Splitter
+ def do_hands_per_file(self, infile, num=-1):
+ done = False
+ n = 0
+ outfile = self.new_file(num)
+ while n < self.hands:
+ try:
+ infile = self.next_hand(infile)
+ infile = self.process_hand(infile, outfile)
+ except FpdbEndOfFile:
+ done = True
+ break
+ except:
+ print _("Unexpected error processing file")
+ sys.exit(2)
+ n += 1
+ outfile.close()
+ if not done:
+ return infile
+ else:
+ return None
+
+ #Non-Archive Hand Splitter
+ def paragraphs(self, file, separator=None, addendum=None):
+ if not callable(separator) and self.line_delimiter:
+ def separator(line): return line == '\n'
+ else:
+ def separator(line): return self.re_SplitHands.search(line)
+ file_str = StringIO()
+ print file_str.getvalue()
+ for line in file:
+ if separator(line+addendum):
+ if file_str.getvalue():
+ if not self.line_delimiter:
+ file_str.write(line)
+ yield file_str.getvalue()
+ file_str = None
+ file_str = StringIO()
+ else:
+ file_str.write(line)
+ if file_str.getvalue(): yield file_str.getvalue()
+ self.filedone = True
+
+
+ # Finds pre-hand header (Hand #)
+ def next_hand(self, infile):
+ m = None
+ while not m:
+ l = infile.readline()
+ #print l, len(l)
+ # Catch EOF
+ if len(l) == 0:
+ raise FpdbEndOfFile(_("End of file reached"))
+ m = re_SplitArchive.search(l)
+ # There is an empty line after pre-hand header and actual HH entry
+ l = infile.readline()
+
+ return infile
+
+ # Each individual hand is written separately
+ def process_hand(self, infile=None, outfile=None):
+ l = infile.readline()
+ l = l.replace('\r\n', '\n')
+ outfile.write(l)
+ l = infile.readline()
+
+ while len(l) < 3:
+ l = infile.readline()
+
+ while len(l) > 2:
+ l = l.replace('\r\n', '\n')
+ outfile.write(l)
+ l = infile.readline()
+ outfile.write(self.line_delimiter)
+ return infile
+
+ def __listof(self, x):
+ if isinstance(x, list) or isinstance(x, tuple):
+ return x
+ else:
+ return [x]
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv[1:]
+
+ if not options.config:
+ options.config = Configuration.Config(file = "HUD_config.test.xml")
+
+ if options.filename:
+ SplitHH = SplitHandHistory(options.config, options.filename, options.outpath, options.hands,
+ options.hhc, options.archive)
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/pyfpdb/TableWindow.py b/pyfpdb/TableWindow.py
index e3ec40de..ec694c73 100644
--- a/pyfpdb/TableWindow.py
+++ b/pyfpdb/TableWindow.py
@@ -184,9 +184,13 @@ class Table_Window(object):
if new_title is None:
return False
- mo = re.search(self.tableno_re, new_title)
+ try:
+ mo = re.search(self.tableno_re, new_title)
+ except AttributeError: #'Table' object has no attribute 'tableno_re'
+ return False
+
if mo is not None:
- print "get_table_no: mo=",mo.groups()
+ #print "get_table_no: mo=",mo.groups()
return mo.group(1)
return False
diff --git a/pyfpdb/WinTables.py b/pyfpdb/WinTables.py
index 442aeaa2..f6ddd93b 100644
--- a/pyfpdb/WinTables.py
+++ b/pyfpdb/WinTables.py
@@ -49,6 +49,7 @@ from TableWindow import Table_Window
b_width = 3
tb_height = 29
+
class Table(Table_Window):
def find_table_parameters(self):
@@ -56,43 +57,50 @@ class Table(Table_Window):
titles = {}
win32gui.EnumWindows(win_enum_handler, titles)
for hwnd in titles:
- if titles[hwnd] == "": continue
+ if titles[hwnd] == "":
+ continue
if re.search(self.search_string, titles[hwnd]):
- if self.check_bad_words(titles[hwnd]): continue
+ if self.check_bad_words(titles[hwnd]):
+ continue
self.window = hwnd
break
try:
if self.window == None:
- log.error( "Window %s not found. Skipping." % self.search_string )
+ log.error(_("Window %s not found. Skipping." % self.search_string))
return None
except AttributeError:
log.error(_("self.window doesn't exist? why?"))
return None
- self.title = titles[hwnd]
- self.hud = None
+ self.title = titles[hwnd]
+ self.hud = None
self.number = hwnd
def get_geometry(self):
- if not win32gui.IsWindow(self.number): # window closed
- return None
-
try:
- (x, y, width, height) = win32gui.GetWindowRect(self.number)
- #print "x=",x,"y=",y,"width=",width,"height=",height
- width = width - x
- height = height - y
- return {'x' : int(x) + b_width,
+ if win32gui.IsWindow(self.number):
+ (x, y, width, height) = win32gui.GetWindowRect(self.number)
+ # this apparently returns x = far left side of window, width = far right side of window, y = top of window, height = bottom of window
+ # so apparently we have to subtract x from "width" to get actual width, and y from "height" to get actual height ?
+ # it definitely gives slightly different results than the GTK code that does the same thing.
+ #print "x=", x, "y=", y, "width=", width, "height=", height
+ width = width - x
+ height = height - y
+ return {
+ 'x' : int(x) + b_width,
'y' : int(y) + tb_height,
'height' : int(height) - y,
'width' : int(width) - x
- }
- except:
+ }
+ except AttributeError:
return None
def get_window_title(self):
- return win32gui.GetWindowText(self.window)
+ try: # after window is destroyed, self.window = attribute error
+ return win32gui.GetWindowText(self.window)
+ except AttributeError:
+ return ""
# def get_nt_exe(self, hwnd):
# """Finds the name of the executable that the given window handle belongs to."""
@@ -145,5 +153,6 @@ class Table(Table_Window):
# hud.main_window.set_title(real_name)
+
def win_enum_handler(hwnd, titles):
titles[hwnd] = win32gui.GetWindowText(hwnd)
diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw
index a6e1719e..ad5ecccf 100755
--- a/pyfpdb/fpdb.pyw
+++ b/pyfpdb/fpdb.pyw
@@ -962,6 +962,7 @@ class fpdb:
if self.db!=None:
if self.db.backend==self.db.MYSQL_INNODB:
try:
+ import _mysql_exceptions
if self.db is not None and self.db.is_connected():
self.db.disconnect()
except _mysql_exceptions.OperationalError: # oh, damn, we're already disconnected
diff --git a/pyfpdb/regression-test-files/cash/Stars/Stud/7-StudHL-USD-0.04-0.08-200911.Cardtest.txt.hp b/pyfpdb/regression-test-files/cash/Stars/Stud/7-StudHL-USD-0.04-0.08-200911.Cardtest.txt.hp
index 65bde96e..6bde0900 100644
--- a/pyfpdb/regression-test-files/cash/Stars/Stud/7-StudHL-USD-0.04-0.08-200911.Cardtest.txt.hp
+++ b/pyfpdb/regression-test-files/cash/Stars/Stud/7-StudHL-USD-0.04-0.08-200911.Cardtest.txt.hp
@@ -312,7 +312,7 @@
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 'S',
- 'raiseFirstInChance': True,
+ 'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
@@ -594,7 +594,7 @@
'otherRaisedStreet3': True,
'otherRaisedStreet4': True,
'position': 6,
- 'raiseFirstInChance': False,
+ 'raiseFirstInChance': True,
'raisedFirstIn': False,
'rake': 2,
'sawShowdown': True,