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

This commit is contained in:
sqlcoder 2009-08-07 00:12:22 +01:00
commit d292cd18d4
7 changed files with 960 additions and 4 deletions

335
pyfpdb/AbsoluteToFpdb.py Normal file
View File

@ -0,0 +1,335 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2008, Carl Gherardi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU 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
########################################################################
# TODO: I have no idea if AP has multi-currency options, i just copied the regex out of Everleaf converter for the currency symbols.. weeeeee - Eric
import sys
import logging
from HandHistoryConverter import *
# Class for converting Absolute HH format.
class Absolute(HandHistoryConverter):
# Static regexes
re_SplitHands = re.compile(r"\n\n\n+")
re_TailSplitHands = re.compile(r"(\n\n\n+)")
#Stage #1571362962: Holdem No Limit $0.02 - 2009-08-05 15:24:06 (ET)
#Table: TORONTO AVE (Real Money) Seat #6 is the dealer
#Seat 6 - FETS63 ($0.75 in chips)
#Board [10s 5d Kh Qh 8c]
re_GameInfo = re.compile(ur"^Stage #([0-9]+): (?P<GAME>Holdem|) (?P<LIMIT>No Limit|) (?P<CURRENCY>\$| €|)(?P<BB>[0-9]*[.0-9]+)", re.MULTILINE)
re_HandInfo = re.compile(ur"^Stage #(?P<HID>[0-9]+): .*(?P<DATETIME>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d).*\nTable: (?P<TABLE>.*) \(Real Money\)", re.MULTILINE)
re_Button = re.compile(ur"Seat #(?P<BUTTON>[0-9]) is the ?[dead]* dealer$", re.MULTILINE) # TODO: that's not the right way to match for "dead" dealer is it?
re_PlayerInfo = re.compile(ur"^Seat (?P<SEAT>[0-9]) - (?P<PNAME>.*) \((?:\$| €|)(?P<CASH>[0-9]*[.0-9]+) in chips\)", re.MULTILINE)
re_Board = re.compile(ur"\[(?P<CARDS>[^\]]*)\]? *$", re.MULTILINE)
# re_GameInfo = re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) ?(?P<GAME>(Holdem|Omaha|7 Card Stud))", re.MULTILINE)
#re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) (?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
# re_HandInfo = re.compile(ur".*#(?P<HID>[0-9]+)\n.*\n(Blinds )?(?:\$| €|)(?P<SB>[.0-9]+)/(?:\$| €|)(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>.+$)", re.MULTILINE)
# re_Button = re.compile(ur"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE)
# re_PlayerInfo = re.compile(ur"^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+((?:\$| €|) (?P<CASH>[.0-9]+) (USD|EUR|)|new player|All-in) \)", re.MULTILINE)
# re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False, index=0):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input
autostart: whether to run the thread (or you can call start() yourself)
debugging: if False, pass on partially supported game types. If true, have a go and error..."""
#print "DEBUG: XXXXXXXXXXXXXXX"
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Absolute", follow=follow, index=index)
logging.info("Initialising Absolute converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.siteId = 3 # Needs to match id entry in Sites database
self.debugging = debugging
if autostart:
self.start()
# otherwise you need to call start yourself.
def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs.
self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
logging.debug("player_re: "+ player_re)
#(?P<CURRENCY>\$| €|)(?P<BB>[0-9]*[.0-9]+)
self.re_PostSB = re.compile(ur"^%s - Posts small blind (?:\$| €|)(?P<SB>[0-9]*[.0-9]+)" % player_re, re.MULTILINE)
self.re_PostBB = re.compile(ur"^%s - Posts big blind (?:\$| €|)(?P<BB>[0-9]*[.0-9]+)" % player_re, re.MULTILINE)
# TODO: Absolute posting when coming in new: %s - Posts $0.02 .. should that be a new Post line? where do we need to add support for that? *confused*
self.re_PostBoth = re.compile(ur"^%s - Posts dead (?:\$| €|)(?P<SBBB>[0-9]*[.0-9]+)" % player_re, re.MULTILINE)
self.re_Action = re.compile(ur"^%s - (?P<ATYPE>Bets |Raises |All-In |All-In\(Raise\) |Calls |Folds|Checks)?\$?(?P<BET>[0-9]*[.0-9]+)?" % player_re, re.MULTILINE)
print "^%s - (?P<ATYPE>Bets |Raises |All-In |All-In\(Raise\) |Calls |Folds|Checks)?\$?(?P<BET>[0-9]*[.0-9]+)?" % player_re
self.re_ShowdownAction = re.compile(ur"^%s - Shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(ur"^Seat [0-9]: %s(?: \(dealer\)| \(big blind\)| \(small blind\)|) (?:won|collected) Total \((?:\$| €|)(?P<POT>[0-9]*[.0-9]+)\)" % player_re, re.MULTILINE)
#self.re_PostSB = re.compile(ur"^%s: posts small blind \[(?:\$| €|) (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
#self.re_PostBB = re.compile(ur"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
#self.re_PostBoth = re.compile(ur"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
#self.re_Antes = re.compile(ur"^%s: posts ante \[(?:\$| €|) (?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
#self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
self.re_HeroCards = re.compile(ur"^Dealt to %s \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds| complete to)(\s\[?(?:\$| €|) ?(?P<BET>\d+\.?\d*)\.?\s?(USD|EUR|)\]?)?" % player_re, re.MULTILINE)
#self.re_ShowdownAction = re.compile(ur"^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
#self.re_CollectPot = re.compile(ur"^%s wins (?:\$| €|) (?P<POT>[.\d]+) (USD|EUR|chips)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE)
#self.re_SitsOut = re.compile(ur"^%s sits out" % player_re, re.MULTILINE)
def readSupportedGames(self):
return [["ring", "hold", "nl"],
["ring", "hold", "pl"],
["ring", "hold", "fl"],
["ring", "studhi", "fl"],
["ring", "omahahi", "pl"]
]
def determineGameType(self, handText):
"""return dict with keys/values:
'type' in ('ring', 'tour')
'limitType' in ('nl', 'cn', 'pl', 'cp', 'fl')
'base' in ('hold', 'stud', 'draw')
'category' in ('holdem', 'omahahi', omahahilo', 'razz', 'studhi', 'studhilo', 'fivedraw', '27_1draw', '27_3draw', 'badugi')
'hilo' in ('h','l','s')
'smallBlind' int?
'bigBlind' int?
'smallBet'
'bigBet'
'currency' in ('USD', 'EUR', 'T$', <countrycode>)
or None if we fail to get the info """
info = {'type':'ring'}
m = self.re_GameInfo.search(handText)
if not m:
return None
mg = m.groupdict()
# translations from captured groups to our info strings
limits = { 'No Limit':'nl', 'PL':'pl', '':'fl' }
games = { # base, category
"Holdem" : ('hold','holdem'),
'Omaha' : ('hold','omahahi'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi')
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
if 'LIMIT' in mg:
info['limitType'] = limits[mg['LIMIT']]
if 'GAME' in mg:
(info['base'], info['category']) = games[mg['GAME']]
if 'SB' in mg:
info['sb'] = mg['SB']
else:
info['sb'] = str(float(mg['BB']) * 0.5) # TODO: Apparently AP doesn't provide small blind info!? must search to see if it's posted, I guess
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']]
if info['currency'] == 'T$':
info['type'] = 'tour'
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
return info
def readHandInfo(self, hand):
m = self.re_HandInfo.search(hand.handText)
if(m == None):
logging.info("Didn't match re_HandInfo")
logging.info(hand.handText)
return None
logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE')))
hand.handid = m.group('HID')
hand.tablename = m.group('TABLE')
hand.maxseats = 6 # assume 6-max unless we have proof it's a larger/smaller game, since absolute doesn't give seat max info
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%Y-%m-%d %H:%M:%S")
return
def readPlayerStacks(self, hand):
m = self.re_PlayerInfo.finditer(hand.handText)
for a in m:
seatnum = int(a.group('SEAT'))
hand.addPlayer(seatnum, a.group('PNAME'), a.group('CASH'))
if seatnum > 6:
hand.maxseats = 10 # absolute does 2/4/6/8/10 games
# TODO: implement lookup list by table-name to determine maxes, then fall back to 6 default/10 here, if there's no entry in the list?
def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
#m = re.search('(\*\* Dealing down cards \*\*\n)(?P<PREFLOP>.*?\n\*\*)?( Dealing Flop \*\* \[ (?P<FLOP1>\S\S), (?P<FLOP2>\S\S), (?P<FLOP3>\S\S) \])?(?P<FLOP>.*?\*\*)?( Dealing Turn \*\* \[ (?P<TURN1>\S\S) \])?(?P<TURN>.*?\*\*)?( Dealing River \*\* \[ (?P<RIVER1>\S\S) \])?(?P<RIVER>.*)', hand.handText,re.DOTALL)
if hand.gametype['base'] == 'hold':
m = re.search(r"\*\*\* POCKET CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
r"(\*\*\* FLOP \*\*\*(?P<FLOP>.+(?=\*\*\* TURN \*\*\*)|.+))?"
r"(\*\*\* TURN \*\*\*(?P<TURN>.+(?=\*\*\* RIVER \*\*\*)|.+))?"
r"(\*\*\* RIVER \*\*\*(?P<RIVER>.+))?", hand.handText, re.DOTALL)
elif hand.gametype['base'] == 'stud': # TODO: Not implemented yet
m = re.search(r"(?P<ANTES>.+(?=\*\* Dealing down cards \*\*)|.+)"
r"(\*\* Dealing down cards \*\*(?P<THIRD>.+(?=\*\*\*\* dealing 4th street \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing 4th street \*\*\*\*(?P<FOURTH>.+(?=\*\*\*\* dealing 5th street \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing 5th street \*\*\*\*(?P<FIFTH>.+(?=\*\*\*\* dealing 6th street \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing 6th street \*\*\*\*(?P<SIXTH>.+(?=\*\*\*\* dealing river \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing river \*\*\*\*(?P<SEVENTH>.+))?", hand.handText,re.DOTALL)
hand.addStreets(m)
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
# If this has been called, street is a street which gets dealt community cards by type hand
# but it might be worth checking somehow.
# if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
logging.debug("readCommunityCards (%s)" % street)
m = self.re_Board.search(hand.streets[street])
cards = m.group('CARDS')
cards = [validCard(card) for card in cards.split(' ')]
hand.setCommunityCards(street=street, cards=cards)
def readAntes(self, hand):
logging.debug("reading antes")
m = self.re_Antes.finditer(hand.handText)
for player in m:
logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
m = self.re_BringIn.search(hand.handText,re.DOTALL)
if m:
logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
else:
logging.warning("No bringin found.")
def readBlinds(self, hand):
m = self.re_PostSB.search(hand.handText)
if m is not None:
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
else:
logging.debug("No small blind")
hand.addBlind(None, None, None)
for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.re_PostBoth.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'both', a.group('SBBB'))
def readButton(self, hand):
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText)
if m:
hand.hero = m.group('PNAME')
# "2c, qh" -> ["2c","qc"]
# Also works with Omaha hands.
cards = m.group('CARDS')
cards = [validCard(card) for card in cards.split(' ')]
# hand.addHoleCards(cards, m.group('PNAME'))
hand.addHoleCards('PREFLOP', hand.hero, closed=cards, shown=False, mucked=False, dealt=True)
else:
#Not involved in hand
hand.involved = False
def readStudPlayerCards(self, hand, street):
# lol. see Plymouth.txt
logging.warning("Absolute readStudPlayerCards is only a stub.")
#~ if street in ('THIRD', 'FOURTH', 'FIFTH', 'SIXTH'):
#~ hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = [], open = [])
def readAction(self, hand, street):
logging.debug("readAction (%s)" % street)
m = self.re_Action.finditer(hand.streets[street])
for action in m:
logging.debug("%s %s" % (action.group('ATYPE'), action.groupdict()))
if action.group('ATYPE') == 'Raises ' or action.group('ATYPE') == 'All-In(Raise) ':
hand.addCallandRaise( 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 ' or action.group('ATYPE') == 'All-In ':
hand.addBet( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'Folds':
hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == 'Checks':
hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == ' complete to': # TODO: not supported yet ?
hand.addComplete( street, action.group('PNAME'), action.group('BET'))
else:
logging.debug("Unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),))
def readShowdownActions(self, hand):
"""Reads lines where holecards are reported in a showdown"""
logging.debug("readShowdownActions")
for shows in self.re_ShowdownAction.finditer(hand.handText):
cards = shows.group('CARDS')
cards = [validCard(card) for card in cards.split(' ')]
logging.debug("readShowdownActions %s %s" %(cards, shows.group('PNAME')))
hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand):
for m in self.re_CollectPot.finditer(hand.handText):
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
def readShownCards(self,hand):
"""Reads lines where hole & board cards are mixed to form a hand (summary lines)"""
for m in self.re_CollectPot.finditer(hand.handText):
try:
if m.group('CARDS') is not None:
cards = m.group('CARDS')
cards = [validCard(card) for card in cards.split(' ')]
player = m.group('PNAME')
logging.debug("readShownCards %s cards=%s" % (player, cards))
# hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
hand.addShownCards(cards=cards, player=m.group('PNAME'))
except IndexError:
pass # there's no "PLAYER - Mucks" at AP that I can see
def validCard(card):
card = card.strip()
if card == '10s': card = 'Ts'
if card == '10h': card = 'Th'
if card == '10d': card = 'Td'
if card == '10c': card = 'Tc'
return card
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="-")
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
parser.add_option("-q", "--quiet",
action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
parser.add_option("-v", "--verbose",
action="store_const", const=logging.INFO, dest="verbosity")
parser.add_option("--vv",
action="store_const", const=logging.DEBUG, dest="verbosity")
(options, args) = parser.parse_args()
LOG_FILENAME = './logging.out'
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
e = Absolute(in_path = options.ipath, out_path = options.opath, follow = options.follow, autostart=True, debugging=True)

View File

@ -957,6 +957,11 @@ class Database:
c.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('Everleaf', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('Win2day', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('OnGame', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('UltimateBet', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('Betfair', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('Absolute', 'USD')")
c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')")
if self.backend == self.SQLITE:
c.execute("INSERT INTO TourneyTypes VALUES (NULL, 1, 0, 0, 0, 0);")
else:

View File

@ -81,7 +81,7 @@ class GuiPlayerStats (threading.Thread):
, ["hand", False, "Hand", 0.0, "%s"] # true not allowed for this line
, ["plposition", False, "Posn", 1.0, "%s"] # true not allowed for this line (set in code)
, ["n", True, "Hds", 1.0, "%d"]
, ["avgseats", True, "Seats", 1.0, "%3.1f"]
, ["avgseats", False, "Seats", 1.0, "%3.1f"]
, ["vpip", True, "VPIP", 1.0, "%3.1f"]
, ["pfr", True, "PFR", 1.0, "%3.1f"]
, ["pf3", True, "PF3", 1.0, "%3.1f"]

View File

@ -203,6 +203,96 @@
<location seat="9" x="70" y="53"> </location>
</layout>
</site>
<site enabled="False"
site_name="Absolute"
table_finder="AbsolutePoker.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path=""
HH_path=""
decoder="everleaf_decode_table"
converter="AbsoluteToFpdb"
supported_games="holdem">
<layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location>
<location seat="3" x="650" y="385"> </location>
<location seat="4" x="588" y="425"> </location>
<location seat="5" x="92" y="425"> </location>
<location seat="6" x="0" y="373"> </location>
<location seat="7" x="0" y="223"> </location>
<location seat="8" x="25" y="50"> </location>
</layout>
<layout fav_seat="0" height="547" max="6" width="794">
<location seat="1" x="640" y="58"> </location>
<location seat="2" x="654" y="288"> </location>
<location seat="3" x="615" y="424"> </location>
<location seat="4" x="70" y="421"> </location>
<location seat="5" x="0" y="280"> </location>
<location seat="6" x="70" y="58"> </location>
</layout>
<layout fav_seat="0" height="547" max="2" width="794">
<location seat="1" x="651" y="288"> </location>
<location seat="2" x="10" y="288"> </location>
</layout>
<layout fav_seat="0" height="547" max="9" width="794">
<location seat="1" x="634" y="38"> </location>
<location seat="2" x="667" y="184"> </location>
<location seat="3" x="667" y="321"> </location>
<location seat="4" x="667" y="445"> </location>
<location seat="5" x="337" y="459"> </location>
<location seat="6" x="0" y="400"> </location>
<location seat="7" x="0" y="322"> </location>
<location seat="8" x="0" y="181"> </location>
<location seat="9" x="70" y="53"> </location>
</layout>
</site>
<site enabled="False"
site_name="PartyPoker"
table_finder="PartyPoker.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path=""
HH_path=""
decoder="everleaf_decode_table"
converter="PartyPokerToFpdb"
supported_games="holdem">
<layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location>
<location seat="3" x="650" y="385"> </location>
<location seat="4" x="588" y="425"> </location>
<location seat="5" x="92" y="425"> </location>
<location seat="6" x="0" y="373"> </location>
<location seat="7" x="0" y="223"> </location>
<location seat="8" x="25" y="50"> </location>
</layout>
<layout fav_seat="0" height="547" max="6" width="794">
<location seat="1" x="640" y="58"> </location>
<location seat="2" x="654" y="288"> </location>
<location seat="3" x="615" y="424"> </location>
<location seat="4" x="70" y="421"> </location>
<location seat="5" x="0" y="280"> </location>
<location seat="6" x="70" y="58"> </location>
</layout>
<layout fav_seat="0" height="547" max="2" width="794">
<location seat="1" x="651" y="288"> </location>
<location seat="2" x="10" y="288"> </location>
</layout>
<layout fav_seat="0" height="547" max="9" width="794">
<location seat="1" x="634" y="38"> </location>
<location seat="2" x="667" y="184"> </location>
<location seat="3" x="667" y="321"> </location>
<location seat="4" x="667" y="445"> </location>
<location seat="5" x="337" y="459"> </location>
<location seat="6" x="0" y="400"> </location>
<location seat="7" x="0" y="322"> </location>
<location seat="8" x="0" y="181"> </location>
<location seat="9" x="70" y="53"> </location>
</layout>
</site>
</supported_sites>
<supported_games>
@ -337,6 +427,8 @@
<hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/>
<hhc site="Everleaf" converter="EverleafToFpdb"/>
<hhc site="Win2day" converter="Win2dayToFpdb"/>
<hhc site="Absolute" converter="AbsoluteToFpdb"/>
<hhc site="PartyPoker" converter="PartyPokerToFpdb"/>
</hhcs>
<supported_databases>

View File

@ -40,7 +40,7 @@ class Hand(object):
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7}
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9 }
def __init__(self, sitename, gametype, handText, builtFrom = "HHC"):

521
pyfpdb/PartyPokerToFpdb.py Normal file
View File

@ -0,0 +1,521 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2009, Grigorij Indigirkin
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
import sys
from collections import defaultdict
from HandHistoryConverter import *
# PartyPoker HH Format
class PartyPoker(HandHistoryConverter):
class ParsingException(Exception):
"Usage: raise ParsingException(<msg>[, hh=<hh>])"
def __init__(self, *args, **kwargs):
if len(args)==0: args=[''] + list(args)
msg, args = args[0], args[1:]
if 'hh' in kwargs:
msg += self.wrapHh(kwargs['hh'])
del kwargs['hh']
return Exception.__init__(self, msg, *args, **kwargs)
def wrapHh(self, hh):
return ("\n\nHand history attached below:\n"
"%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \
{'DELIMETER': '#'*50, 'HH': hh}
############################################################
# Class Variables
sym = {'USD': "\$", }
# Static regexes
# $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009
# NL Texas Hold'em $1 USD Buy-in Trny:45685440 Level:8 Blinds-Antes(600/1 200 -50) - Sunday, May 17, 11:25:07 MSKS 2009
re_GameInfoRing = re.compile("""
(?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>\d+)\s*(?:USD)?\s*
(?P<LIMIT>(NL))\s+
(?P<GAME>(Texas\ Hold\'em))
\s*\-\s*
(?P<DATETIME>.+)
""", re.VERBOSE)
re_GameInfoTrny = re.compile("""
(?P<LIMIT>(NL))\s+
(?P<GAME>(Texas\ Hold\'em))\s+
(?P<BUYIN>\$?[.0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+
Trny:\s?(?P<TOURNO>\d+)\s+
Level:\s*(?P<LEVEL>\d+)\s+
Blinds(?:-Antes)?\(
(?P<SB>[.0-9 ]+)\s*
/(?P<BB>[.0-9 ]+)
(?:\s*-\s*(?P<ANTE>[.0-9 ]+)\$?)?
\)
\s*\-\s*
(?P<DATETIME>.+)
""", re.VERBOSE)
re_Hid = re.compile("^Game \#(?P<HID>\d+) starts.")
#re_GameInfo = re.compile("""
#PartyPoker\sGame\s\#(?P<HID>[0-9]+):\s+
#(Tournament\s\# # open paren of tournament info
#(?P<TOURNO>\d+),\s
#(?P<BUYIN>[%(LS)s\+\d\.]+ # here's how I plan to use LS
#\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?
#)\s)? # close paren of tournament info
#(?P<MIXED>HORSE|8\-Game|HOSE)?\s?\(?
#(?P<GAME>Hold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball)\s
#(?P<LIMIT>No\sLimit|Limit|Pot\sLimit)\)?,?\s
#(-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)?
#\(? # open paren of the stakes
#(?P<CURRENCY>%(LS)s|)?
#(?P<SB>[.0-9]+)/(%(LS)s)?
#(?P<BB>[.0-9]+)
#\s?(?P<ISO>%(LEGAL_ISO)s)?
#\)\s-\s # close paren of the stakes
#(?P<DATETIME>.*$)""" % substitutions,
#re.MULTILINE|re.VERBOSE)
re_PlayerInfo = re.compile("""
Seat\s(?P<SEAT>\d+):\s
(?P<PNAME>.*)\s
\(\s*\$?(?P<CASH>[0-9,.]+)\s*(?:USD|)\s*\)
""" ,
re.VERBOSE)
#re_PlayerInfo = re.compile("""
#^Seat\s(?P<SEAT>[0-9]+):\s
#(?P<PNAME>.*)\s
#\((%(LS)s)?(?P<CASH>[.0-9]+)\sin\schips\)""" % substitutions,
#re.MULTILINE|re.VERBOSE)
re_HandInfo = re.compile("""
^Table\s+
(?P<TTYPE>[a-zA-Z0-9 ]+)\s+
(?: \#|\(|)(?P<TABLE>\d+)\)?\s+
(?:[^ ]+\s+\#(?P<MTTTABLE>\d+).+)? # table number for mtt
\((?P<PLAY>Real|Play)\s+Money\)\s+ # FIXME: check if play money is correct
Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
""",
re.MULTILINE|re.VERBOSE)
#re_HandInfo = re.compile("""
#^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s
#((?P<MAX>\d+)-max\s)?
#(?P<PLAY>\(Play\sMoney\)\s)?
#(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
#re.MULTILINE|re.VERBOSE)
re_SplitHands = re.compile('\x00+')
re_TailSplitHands = re.compile('(\x00+)')
lineSplitter = '\n'
re_Button = re.compile('Seat (?P<BUTTON>\d+) is the button', re.MULTILINE)
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
re_NoSmallBlind = re.compile('^There is no Small Blind in this hand as the Big Blind of the previous hand left the table')
# self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="PartyPoker", follow=follow, index=index)
logging.info("Initialising PartyPoker converter class")
self.filetype = "text"
self.codepage = "cp1252" # FIXME: wtf?
self.siteId = 9 # Needs to match id entry in Sites database
self._gameType = None # cached reg-parse result
if autostart:
self.start()
def allHandsAsList(self):
list = HandHistoryConverter.allHandsAsList(self)
return filter(lambda text: len(text.strip()), list)
def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs.
# TODO: should probably rename re_HeroCards and corresponding method,
# since they are used to find all cards on lines starting with "Dealt to:"
# They still identify the hero.
self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
subst = {'PLYR': player_re, 'CUR_SYM': hand.SYMBOL[hand.gametype['currency']],
'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''}
logging.debug("player_re: " + subst['PLYR'])
logging.debug("CUR_SYM: " + subst['CUR_SYM'])
logging.debug("CUR: " + subst['CUR'])
self.re_PostSB = re.compile(
r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.0-9]+) ?%(CUR)s\]\." % subst,
re.MULTILINE)
self.re_PostBB = re.compile(
r"^%(PLYR)s posts big blind \[%(CUR_SYM)s(?P<BB>[.0-9]+) ?%(CUR)s\]\." % subst,
re.MULTILINE)
self.re_Antes = re.compile(
r"^%(PLYR)s posts ante \[%(CUR_SYM)s(?P<ANTE>[.0-9]+) ?%(CUR)s\]\." % subst,
re.MULTILINE)
#self.re_BringIn = re.compile(
#r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst,
#re.MULTILINE)
#self.re_PostBoth = re.compile(
#r"^%(PLYR)s: posts small \& big blinds \[%(CUR)s (?P<SBBB>[.0-9]+)" % subst,
#re.MULTILINE)
self.re_HeroCards = re.compile(
r"^Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst,
re.MULTILINE)
self.re_Action = re.compile(r"""
^%(PLYR)s\s+(?P<ATYPE>bets|checks|raises|calls|folds|is\sall-In)
(?:\s+\[%(CUR_SYM)s(?P<BET>[.,\d]+)\s*%(CUR)s\])?
""" % subst,
re.MULTILINE|re.VERBOSE)
self.re_ShownCards = re.compile(
r"^%s (?P<SHOWED>(?:doesn\'t )?shows?) " % player_re +
r"\[ *(?P<CARDS>.+) *\](?P<COMBINATION>.+)\.",
re.MULTILINE)
self.re_CollectPot = re.compile(
r""""^%(PLYR)s \s+ wins \s+
%(CUR_SYM)s(?P<POT>[.\d]+)\s*%(CUR)s""" % subst,
re.MULTILINE|re.VERBOSE)
#self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
#self.re_ShownCards = re.compile(
#"^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?
#(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re,
#re.MULTILINE)
def readSupportedGames(self):
return [["ring", "hold", "nl"],
#["ring", "hold", "pl"],
#["ring", "hold", "fl"],
["tour", "hold", "nl"],
#["tour", "hold", "pl"],
#["tour", "hold", "fl"],
]
def _getGameType(self, handText):
if self._gameType is None:
# let's determine whether hand is trny
# and whether 5-th line contains head line
headLine = handText.split(self.lineSplitter)[4]
#print headLine
#sys.exit(1)
for headLineContainer in headLine, handText:
for regexp in self.re_GameInfoTrny, self.re_GameInfoRing:
m = regexp.search(headLineContainer)
if m is not None:
self._gameType = m
return self._gameType
return self._gameType
def determineGameType(self, handText):
# inspect the handText and return the gametype dict
# gametype dict is:
# {'limitType': xxx, 'base': xxx, 'category': xxx}
print self.ParsingException().wrapHh( handText )
info = {}
m = self._getGameType(handText)
if m is None:
return None
mg = m.groupdict()
# translations from captured groups to fpdb info strings
limits = { 'NL':'nl',
# 'Pot Limit':'pl', 'Limit':'fl'
}
games = { # base, category
"Texas Hold'em" : ('hold','holdem'),
#'Omaha' : ('hold','omahahi'),
}
currencies = { '$':'USD', '':'T$' }
for expectedField in ['LIMIT', 'GAME']:
if mg[expectedField] is None:
raise self.ParsingException(
"Cannot fetch field '%s'" % expectedField,
hh = handText)
try:
info['limitType'] = limits[mg['LIMIT']]
except:
raise self.ParsingException(
"Unknown limit '%s'" % mg['LIMIT'],
hh = handText)
try:
(info['base'], info['category']) = games[mg['GAME']]
except:
raise self.ParsingException(
"Unknown game type '%s'" % mg['GAME'],
hh = handText)
if 'TOURNO' in mg:
info['type'] = 'tour'
else:
info['type'] = 'ring'
if info['type'] == 'ring':
info['sb'], info['bb'] = ringBlinds(mg['RINGLIMIT'])
# FIXME: there are only $ and play money availible for cash
info['currency'] = currencies(mg['CURRENCY'])
else:
info['sb'] = renderTrnyMoney(mg['SB'])
info['bb'] = renderTrnyMoney(mg['BB'])
info['currency'] = 'T$'
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
return info
def readHandInfo(self, hand):
info = {}
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if m:
info.update(m.groupdict())
else:
raise self.ParsingException("Cannot read Handinfo for current hand", hh=hand.handText)
m = self._getGameType(hand.handText)
if m: info.update(m.groupdict())
m = self.re_Hid.search(hand.handText)
if m: info.update(m.groupdict())
# FIXME: it's a hack cause party doesn't supply hand.maxseats info
#hand.maxseats = '9'
hand.mixed = None
# TODO : I rather like the idea of just having this dict as hand.info
logging.debug("readHandInfo: %s" % info)
for key in info:
if key == 'DATETIME':
#Saturday, July 25, 07:53:52 EDT 2009
#Thursday, July 30, 21:40:41 MSKS 2009
m2 = re.search("\w+, (?P<M>\w+) (?P<D>\d+), (?P<H>\d+):(?P<MIN>\d+):(?P<S>\d+) (?P<TZ>[A-Z]+) (?P<Y>\d+)", info[key])
# we cant use '%B' due to locale problems
months = ['January', 'February', 'March', 'April','May', 'June',
'July','August','September','October','November','December']
month = months.index(m2.group('M')) + 1
datetimestr = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), month,m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S'))
hand.starttime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
#tzShift = defaultdict(lambda:0, {'EDT': -5, 'EST': -6, 'MSKS': 3})
#hand.starttime -= datetime.timedelta(hours=tzShift[m2.group('TZ')])
if key == 'HID':
hand.handid = info[key]
if key == 'TABLE':
hand.tablename = info[key]
if key == 'BUTTON':
hand.buttonpos = info[key]
if key == 'TOURNO':
hand.tourNo = info[key]
if key == 'BUYIN':
#FIXME: it's dirty hack T_T
cur = info[key][0] if info[key][0] not in '0123456789' else ''
hand.buyin = info[key] + '+%s0' % cur
if key == 'LEVEL':
hand.level = info[key]
if key == 'PLAY' and info['PLAY'] != 'Real':
# TODO: play money wasn't tested
# hand.currency = 'play' # overrides previously set value
hand.gametype['currency'] = 'play'
def readButton(self, hand):
m = self.re_Button.search(hand.handText)
if m:
hand.buttonpos = int(m.group('BUTTON'))
else:
logging.info('readButton: not found')
def readPlayerStacks(self, hand):
logging.debug("readPlayerStacks")
m = self.re_PlayerInfo.finditer(hand.handText)
players = []
for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'),
renderTrnyMoney(a.group('CASH')))
def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
assert hand.gametype['base'] == "hold", \
"wtf! There're no %s games on party" % hand.gametype['base']
m = re.search(
r"\*{2} Dealing down cards \*{2}"
r"(?P<PREFLOP>.+?)"
r"(?:\*{2} Dealing Flop \*{2} (?P<FLOP>\[ \S\S, \S\S, \S\S \].+?))?"
r"(?:\*{2} Dealing Turn \*{2} (?P<TURN>\[ \S\S \].+?))?"
r"(?:\*{2} Dealing River \*{2} (?P<RIVER>\[ \S\S \].+?))?$"
, hand.handText,re.DOTALL)
hand.addStreets(m)
def readCommunityCards(self, hand, street):
if street in ('FLOP','TURN','RIVER'):
m = self.re_Board.search(hand.streets[street])
hand.setCommunityCards(street, renderCards(m.group('CARDS')))
def readAntes(self, hand):
logging.debug("reading antes")
m = self.re_Antes.finditer(hand.handText)
for player in m:
#~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
m = self.re_BringIn.search(hand.handText,re.DOTALL)
if m:
#~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
def readBlinds(self, hand):
noSmallBlind = bool(self.re_NoSmallBlind.search(hand.handText))
if hand.gametype['type'] == 'ring':
try:
assert noSmallBlind==False
m = self.re_PostSB.search(hand.handText)
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind
hand.addBlind(None, None, None)
for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
else:
# party doesn't track blinds for tournaments
# so there're some cra^Wcaclulations
if hand.buttonpos == 0:
self.readButton(hand)
# NOTE: code below depends on Hand's implementation
# playersMap - dict {seat: (pname,stack)}
playersMap = dict([(f[0], f[1:3]) for f in hand.players])
maxSeat = max(playersMap)
def findFirstNonEmptySeat(startSeat):
while startSeat not in playersMap:
if startSeat >= maxSeat:
startSeat = 0
startSeat += 1
return startSeat
smartMin = lambda A,B: A if float(A) <= float(B) else B
if noSmallBlind:
hand.addBlind(None, None, None)
else:
smallBlindSeat = findFirstNonEmptySeat(int(hand.buttonpos) + 1)
blind = smartMin(hand.sb, playersMap[smallBlindSeat][1])
hand.addBlind(playersMap[smallBlindSeat][0], 'small blind', blind)
bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1)
blind = smartMin(hand.bb, playersMap[bigBlindSeat][1])
hand.addBlind(playersMap[bigBlindSeat][0], 'small blind', blind)
def readHeroCards(self, hand):
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
# we need to grab hero's cards
for street in ('PREFLOP',):
if street in hand.streets.keys():
m = self.re_HeroCards.finditer(hand.streets[street])
for found in m:
# if m == None:
# hand.involved = False
# else:
hand.hero = found.group('PNAME')
newcards = renderCards(found.group('NEWCARDS'))
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street])
for action in m:
acts = action.groupdict()
if action.group('ATYPE') in ('raises','is all-In'):
#print action.groupdict()
#sys.exit(1)
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'calls':
hand.addCall( street, action.group('PNAME'), action.group('BET') )
#print action.groupdict()
#sys.exit(1)
elif action.group('ATYPE') == 'bets':
hand.addBet( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'folds':
hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == 'checks':
hand.addCheck( street, action.group('PNAME'))
else:
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
def readShowdownActions(self, hand):
# all action in readShownCards
pass
## TODO: pick up mucks also??
#for shows in self.re_ShowdownAction.finditer(hand.handText):
#cards = shows.group('CARDS').split(' ')
#hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand):
for m in self.re_CollectPot.finditer(hand.handText):
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
def readShownCards(self,hand):
for m in self.re_ShownCards.finditer(hand.handText):
if m.group('CARDS') is not None:
cards = renderCards(m.group('CARDS'))
(shown, mucked) = (False, False)
if m.group('SHOWED') == "show": shown = True
else: mucked = True
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked)
def ringBlinds(ringLimit):
"Returns blinds for current limit"
ringLimit = float(ringLimit)
if ringLimit == 5.: ringLimit = 4.
return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) )
def renderTrnyMoney(money):
"renders 'numbers' like '1 200' and '2,000'"
return money.replace(' ', '').replace(',', '')
def renderCards(string):
"splits strings like ' Js, 4d '"
cards = string.strip().split(' ')
return filter(len, map(lambda x: x.strip(' ,'), cards))
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/stars/horse/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt")
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
parser.add_option("-q", "--quiet",
action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
parser.add_option("-v", "--verbose",
action="store_const", const=logging.INFO, dest="verbosity")
parser.add_option("--vv",
action="store_const", const=logging.DEBUG, dest="verbosity")
(options, args) = parser.parse_args()
#LOG_FILENAME = './logging.out'
logging.basicConfig(level=options.verbosity)
e = PartyPoker(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -132,6 +132,9 @@ class Importer:
# self.updated = time()
def clearFileList(self):
self.updatedsize = {}
self.updatetime = {}
self.pos_in_file = {}
self.filelist = {}
def closeDBs(self):
@ -199,7 +202,7 @@ class Importer:
print "Started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes']
if self.settings['dropIndexes'] == 'auto':
self.settings['dropIndexes'] = self.calculate_auto2(self.database, 12.0, 500.0)
if self.settings['dropHudCache'] == 'auto':
if 'dropHudCache' in self.settings and self.settings['dropHudCache'] == 'auto':
self.settings['dropHudCache'] = self.calculate_auto2(self.database, 25.0, 500.0) # returns "drop"/"don't drop"
if self.settings['dropIndexes'] == 'drop':
@ -241,7 +244,7 @@ class Importer:
self.database.afterBulkImport()
else:
print "No need to rebuild indexes."
if self.settings['dropHudCache'] == 'drop':
if 'dropHudCache' in self.settings and self.settings['dropHudCache'] == 'drop':
self.database.rebuild_hudcache()
else:
print "No need to rebuild hudcache."