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,