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

Conflicts:
	pyfpdb/PokerStarsToFpdb.py
This commit is contained in:
Ray 2009-07-15 13:36:00 -04:00
commit 32a1c322d4
12 changed files with 631 additions and 128 deletions

View File

@ -521,6 +521,8 @@ class Config:
db['db-backend'] = 2 db['db-backend'] = 2
elif string.lower(self.supported_databases[name].db_server) == 'postgresql': elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
db['db-backend'] = 3 db['db-backend'] = 3
elif string.lower(self.supported_databases[name].db_server) == 'sqlite':
db['db-backend'] = 4
else: db['db-backend'] = None # this is big trouble else: db['db-backend'] = None # this is big trouble
return db return db

View File

@ -71,6 +71,7 @@ class Database:
# a new session) # a new session)
cur = self.connection.cursor() cur = self.connection.cursor()
if self.fdb.wrongDbVersion == False:
self.hand_1day_ago = 0 self.hand_1day_ago = 0
cur.execute(self.sql.query['get_hand_1day_ago']) cur.execute(self.sql.query['get_hand_1day_ago'])
row = cur.fetchone() row = cur.fetchone()
@ -85,6 +86,8 @@ class Database:
self.hand_nhands_ago = 0 # todo self.hand_nhands_ago = 0 # todo
#cur.execute(self.sql.query['get_table_name'], (hand_id, )) #cur.execute(self.sql.query['get_table_name'], (hand_id, ))
#row = cur.fetchone() #row = cur.fetchone()
else:
print "Bailing on DB query, not sure it exists yet"
self.saveActions = False if self.import_options['saveActions'] == False else True self.saveActions = False if self.import_options['saveActions'] == False else True
def do_connect(self, c): def do_connect(self, c):

View File

@ -38,7 +38,9 @@ class FpdbSQLQueries:
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['list_tables'] = """SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'""" self.query['list_tables'] = """SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['list_tables'] = """ """ self.query['list_tables'] = """SELECT name FROM sqlite_master
WHERE type='table'
ORDER BY name;"""
################################################################## ##################################################################
# Drop Tables - MySQL, PostgreSQL and SQLite all share same syntax # Drop Tables - MySQL, PostgreSQL and SQLite all share same syntax
@ -63,8 +65,8 @@ class FpdbSQLQueries:
self.query['createSettingsTable'] = """CREATE TABLE Settings (version SMALLINT)""" self.query['createSettingsTable'] = """CREATE TABLE Settings (version SMALLINT)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
#Probably doesn't work. self.query['createSettingsTable'] = """CREATE TABLE Settings
self.query['createSettingsTable'] = """ """ (version INTEGER) """
################################ ################################
@ -83,7 +85,10 @@ class FpdbSQLQueries:
name varchar(32), name varchar(32),
currency char(3))""" currency char(3))"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['createSitesTable'] = """ """ self.query['createSitesTable'] = """CREATE TABLE Sites (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
currency TEXT NOT NULL)"""
################################ ################################
@ -118,7 +123,19 @@ class FpdbSQLQueries:
smallBet int, smallBet int,
bigBet int)""" bigBet int)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['createGametypesTable'] = """ """ self.query['createGametypesTable'] = """CREATE TABLE GameTypes (
id INTEGER PRIMARY KEY,
siteId INTEGER,
type TEXT,
base TEXT,
category TEXT,
limitType TEXT,
hiLo TEXT,
smallBlind INTEGER,
bigBlind INTEGER,
smallBet INTEGER,
bigBet INTEGER,
FOREIGN KEY(siteId) REFERENCES Sites(id) ON DELETE CASCADE)"""
################################ ################################
@ -141,7 +158,13 @@ class FpdbSQLQueries:
comment text, comment text,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['createPlayersTable'] = """ """ self.query['createPlayersTable'] = """CREATE TABLE Players (
id INTEGER PRIMARY KEY,
name TEXT,
siteId INTEGER,
comment TEXT,
commentTs BLOB,
FOREIGN KEY(siteId) REFERENCES Sites(id) ON DELETE CASCADE)"""
################################ ################################
@ -245,7 +268,18 @@ class FpdbSQLQueries:
comment TEXT, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['createHandsTable'] = """ """ self.query['createHandsTable'] = """CREATE TABLE Hands (
id INTEGER PRIMARY KEY,
tableName TEXT(20),
siteHandNo INTEGER,
gametypeId INTEGER,
handStart BLOB,
importTime BLOB,
seats INTEGER,
maxSeats INTEGER,
comment TEXT,
commentTs BLOB,
FOREIGN KEY(gametypeId) REFERENCES Gametypes(id) ON DELETE CASCADE)"""
################################ ################################
@ -299,7 +333,14 @@ class FpdbSQLQueries:
comment TEXT, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['createTourneysTable'] = """ """ self.query['createTourneysTable'] = """CREATE TABLE TourneyTypes (
id INTEGER PRIMARY KEY,
siteId INTEGER,
buyin INTEGER,
fee INTEGER,
knockout INTEGER,
rebuyOrAddon BOOL,
FOREIGN KEY(siteId) REFERENCES Sites(id) ON DELETE CASCADE)"""
################################ ################################
# Create HandsPlayers # Create HandsPlayers
@ -832,6 +873,13 @@ class FpdbSQLQueries:
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['addPlayersIndex'] = """ """ self.query['addPlayersIndex'] = """ """
if(self.dbname == 'MySQL InnoDB' or self.dbname == 'PostgreSQL'):
self.query['set tx level'] = """SET SESSION TRANSACTION
ISOLATION LEVEL READ COMMITTED"""
elif(self.dbname == 'SQLite'):
self.query['set tx level'] = """ """
################################ ################################
# Queries used in GuiGraphViewer # Queries used in GuiGraphViewer
################################ ################################

View File

@ -154,7 +154,7 @@ class GuiGraphViewer (threading.Thread):
if not sitenos: if not sitenos:
#Should probably pop up here. #Should probably pop up here.
print "No sites selected - defaulting to PokerStars" print "No sites selected - defaulting to PokerStars"
sitenos = [2] return
if not playerids: if not playerids:
print "No player ids found" print "No player ids found"

View File

@ -159,6 +159,50 @@
<location seat="9" x="70" y="53"> </location> <location seat="9" x="70" y="53"> </location>
</layout> </layout>
</site> </site>
<site enabled="False"
site_name="Win2day"
table_finder="Win2day.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path=""
HH_path=""
decoder="everleaf_decode_table"
converter="Win2dayToFpdb"
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_sites>
<supported_games> <supported_games>
@ -292,6 +336,7 @@
<hhc site="PokerStars" converter="PokerStarsToFpdb"/> <hhc site="PokerStars" converter="PokerStarsToFpdb"/>
<hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/> <hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/>
<hhc site="Everleaf" converter="EverleafToFpdb"/> <hhc site="Everleaf" converter="EverleafToFpdb"/>
<hhc site="Win2day" converter="Win2dayToFpdb"/>
</hhcs> </hhcs>
<supported_databases> <supported_databases>
@ -300,4 +345,3 @@
</FreePokerToolsConfig> </FreePokerToolsConfig>

View File

@ -286,7 +286,6 @@ If a player has None chips he won't be added."""
def setCommunityCards(self, street, cards): def setCommunityCards(self, street, cards):
logging.debug("setCommunityCards %s %s" %(street, cards)) logging.debug("setCommunityCards %s %s" %(street, cards))
self.board[street] = [self.card(c) for c in cards] self.board[street] = [self.card(c) for c in cards]
# print "DEBUG: self.board: %s" % self.board
def card(self,c): def card(self,c):
"""upper case the ranks but not suits, 'atjqk' => 'ATJQK'""" """upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""

View File

@ -28,54 +28,17 @@ import codecs
from decimal import Decimal from decimal import Decimal
import operator import operator
from xml.dom.minidom import Node from xml.dom.minidom import Node
# from pokereval import PokerEval
import time import time
import datetime import datetime
import gettext
#from pokerengine.pokercards import *
# provides letter2name{}, letter2names{}, visible_card(), not_visible_card(), is_visible(), card_value(), class PokerCards
# but it's probably not installed so here are the ones we may want:
letter2name = {
'A': 'Ace',
'K': 'King',
'Q': 'Queen',
'J': 'Jack',
'T': 'Ten',
'9': 'Nine',
'8': 'Eight',
'7': 'Seven',
'6': 'Six',
'5': 'Five',
'4': 'Four',
'3': 'Trey',
'2': 'Deuce'
}
letter2names = {
'A': 'Aces',
'K': 'Kings',
'Q': 'Queens',
'J': 'Jacks',
'T': 'Tens',
'9': 'Nines',
'8': 'Eights',
'7': 'Sevens',
'6': 'Sixes',
'5': 'Fives',
'4': 'Fours',
'3': 'Treys',
'2': 'Deuces'
}
import gettext import gettext
gettext.install('myapplication') gettext.install('fpdb')
class HandHistoryConverter(): class HandHistoryConverter():
READ_CHUNK_SIZE = 10000 # bytes to read at a time from file (in tail mode) READ_CHUNK_SIZE = 10000 # bytes to read at a time from file (in tail mode)
def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False): def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False):
logging.info("HandHistory init called") logging.info("HandHistory init")
# default filetype and codepage. Subclasses should set these properly. # default filetype and codepage. Subclasses should set these properly.
self.filetype = "text" self.filetype = "text"
@ -83,36 +46,33 @@ class HandHistoryConverter():
self.in_path = in_path self.in_path = in_path
self.out_path = out_path self.out_path = out_path
if self.out_path == '-':
# write to stdout if in_path == '-':
self.in_fh = sys.stdin
if out_path == '-':
self.out_fh = sys.stdout self.out_fh = sys.stdout
else: else:
# TODO: out_path should be sanity checked before opening. Perhaps in fpdb_import? # TODO: out_path should be sanity checked.
# I'm not sure what we're looking for, although we don't want out_path==in_path!='-' self.out_fh = open(self.out_path, 'w')
self.out_fh = open(self.out_path, 'w') # doomswitch is now on :|
self.sitename = sitename self.sitename = sitename
self.follow = follow self.follow = follow
self.compiledPlayers = set() self.compiledPlayers = set()
self.maxseats = 10 self.maxseats = 10
def __str__(self): def __str__(self):
#TODO : I got rid of most of the hhdir stuff. return """
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename) HandHistoryConverter: '%(sitename)s'
#tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase) filetype: '%(filetype)s'
#tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir) in_path: '%(in_path)s'
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype) out_path: '%(out_path)s'
tmp = tmp + "\tinfile: '%s'\n" % (self.in_path) """ % { 'sitename':self.sitename, 'filetype':self.filetype, 'in_path':self.in_path, 'out_path':self.out_path }
tmp = tmp + "\toutfile: '%s'\n" % (self.out_path)
#tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
#tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
#tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
#tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
return tmp
def start(self): def start(self):
"""process a hand at a time from the input specified by in_path. """process a hand at a time from the input specified by in_path.
If in follow mode, wait for more data to turn up. If in follow mode, wait for more data to turn up.
Otherwise, finish at eof... Otherwise, finish at eof.
""" """
starttime = time.time() starttime = time.time()
@ -224,7 +184,6 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
base = gametype['base'] base = gametype['base']
limit = gametype['limitType'] limit = gametype['limitType']
l = [type] + [base] + [limit] l = [type] + [base] + [limit]
hand = None
if l in self.readSupportedGames(): if l in self.readSupportedGames():
hand = None hand = None
if gametype['base'] == 'hold': if gametype['base'] == 'hold':

View File

@ -298,8 +298,7 @@ follow : whether to tail -f the input"""
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True) hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
for street, text in hand.streets.iteritems(): for street, text in hand.streets.iteritems():
if street in ('PREFLOP', 'DEAL'): continue # already done these if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
if hand.streets[street] == None: continue # don't regex a None
m = self.re_HeroCards.finditer(hand.streets[street]) m = self.re_HeroCards.finditer(hand.streets[street])
for found in m: for found in m:
player = found.group('PNAME') player = found.group('PNAME')
@ -422,7 +421,7 @@ follow : whether to tail -f the input"""
if __name__ == "__main__": if __name__ == "__main__":
parser = OptionParser() parser = OptionParser()
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/pokerstars/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt") 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("-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("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
parser.add_option("-q", "--quiet", parser.add_option("-q", "--quiet",

384
pyfpdb/Win2dayToFpdb.py Executable file
View File

@ -0,0 +1,384 @@
#!/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
########################################################################
import sys
from HandHistoryConverter import *
# Win2day HH Format
class Win2day(HandHistoryConverter):
# Static regexes
#<HISTORY ID="102271403" SESSION="session31237702.xml" TABLE="Innsbruck 3" GAME="GAME_THM" GAMETYPE="GAMETYPE_REAL" GAMEKIND="GAMEKIND_CASH" TABLECURRENCY="EUR" LIMIT="NL" STAKES="0.25/0.50" DATE="1246909773" WIN="0.00" LOSS="0.50">
#'^<HISTORY ID="(?P<HID>[0-9]+)" SESSION="session[0-9]+\.xml" TABLE="(?P<TABLE>[- a-zA-Z0-9]+)" GAME="(?P<GAME>[_A-Z]+)" GAMETYPE="[_a-zA-Z]+" GAMEKIND="[_a-zA-Z]+" TABLECURRENCY="(?P<CURRENCY>[A-Z]+)" LIMIT="(?P<LIMIT>NL|PL)" STAKES="(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)" DATE="(?P<DATETIME>[0-9]+)" WIN="[.0-9]+" LOSS="[.0-9]+">$'
re_GameInfo = re.compile('^<HISTORY ID="(?P<HID>[0-9]+)" SESSION="session[0-9]+\.xml" TABLE="(?P<TABLE>[- a-zA-Z0-9]+)" GAME="(?P<GAME>[_A-Z]+)" GAMETYPE="[_a-zA-Z]+" GAMEKIND="[_a-zA-Z]+" TABLECURRENCY="(?P<CURRENCY>[A-Z]+)" LIMIT="(?P<LIMIT>NL|PL)" STAKES="(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)" DATE="(?P<DATETIME>[0-9]+)" WIN="[.0-9]+" LOSS="[.0-9]+">', re.MULTILINE)
re_SplitHands = re.compile('</HISTORY>')
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
re_Button = re.compile('<ACTION TYPE="HAND_DEAL" PLAYER="(?P<BUTTON>[^"]+)">\n<CARD LINK="[0-9b]+"></CARD>\n<CARD LINK="[0-9b]+"></CARD></ACTION>\n<ACTION TYPE="ACTION_', re.MULTILINE)
#<PLAYER NAME="prato" SEAT="1" AMOUNT="61.29"></PLAYER>
re_PlayerInfo = re.compile('^<PLAYER NAME="(?P<PNAME>.*)" SEAT="(?P<SEAT>[0-9]+)" AMOUNT="(?P<CASH>[.0-9]+)"></PLAYER>', re.MULTILINE)
re_Card = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD>', re.MULTILINE)
re_BoardLast = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD></ACTION>', re.MULTILINE)
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Win2day", follow=follow)
logging.info("Initialising Win2day converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.sideID = 4
if autostart:
self.start()
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)
#<ACTION TYPE="HAND_BLINDS" PLAYER="prato" KIND="HAND_SB" VALUE="0.25"></ACTION>
self.re_PostSB = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_SB" VALUE="(?P<SB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
self.re_PostBB = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_BB" VALUE="(?P<BB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
self.re_Antes = re.compile(r"^%s: posts the ante \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
self.re_PostBoth = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_AB" VALUE="(?P<SBBB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
#r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n<CARD LINK="(?P<CARD1>[0-9]+)"></CARD>\n<CARD LINK="(?P<CARD2>[0-9]+)"></CARD></ACTION>'
self.re_HeroCards = re.compile(r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]"></CARD>)</ACTION>' % player_re, re.MULTILINE)
#'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>'
self.re_Action = re.compile(r'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(r'<RESULT PLAYER="%s" WIN="[.0-9]+" HAND="(?P<HAND>\(\$STR_G_FOLD\)|[\$\(\)_ A-Z]+)">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]+"></CARD>)</RESULT>' % player_re, re.MULTILINE)
#<RESULT PLAYER="wig0r" WIN="4.10" HAND="$(STR_G_WIN_TWOPAIR) $(STR_G_CARDS_TENS) $(STR_G_ANDTEXT) $(STR_G_CARDS_EIGHTS)">
#
self.re_CollectPot = re.compile(r'<RESULT PLAYER="%s" WIN="(?P<POT>[.\d]+)" HAND=".+">' % player_re, re.MULTILINE)
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
def readSupportedGames(self):
return [["ring", "hold", "nl"],
["ring", "hold", "pl"],
["ring", "hold", "fl"],
["ring", "stud", "fl"],
["ring", "draw", "fl"],
["ring", "omaha", "pl"]
]
def determineGameType(self, handText):
info = {'type':'ring'}
m = self.re_GameInfo.search(handText)
if not m:
print "determineGameType:", handText
return None
mg = m.groupdict()
# translations from captured groups to our info strings
#limits = { 'NL':'nl', 'PL':'pl', 'Limit':'fl' }
limits = { 'NL':'nl', 'PL':'pl'}
games = { # base, category
"GAME_THM" : ('hold','holdem'),
# 'Omaha' : ('hold','omahahi'),
#'Omaha Hi/Lo' : ('hold','omahahilo'),
# 'Razz' : ('stud','razz'),
#'7 Card Stud' : ('stud','studhi'),
# 'Badugi' : ('draw','badugi')
}
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']
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = mg['CURRENCY']
# 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())
# TODO: Be less lazy and parse maxseats from the HandInfo regex
if m.group('TABLEATTRIBUTES'):
m2 = re.search("\s*(\d+)-max", m.group('TABLEATTRIBUTES'))
hand.maxseats = int(m2.group(1))
m = self.re_GameInfo.search(hand.handText)
if m: info.update(m.groupdict())
m = self.re_Button.search(hand.handText)
if m: info.update(m.groupdict())
# TODO : I rather like the idea of just having this dict as hand.info
logging.debug("readHandInfo: %s" % info)
for key in info:
if key == 'DATETIME':
# Win2day uses UTC timestamp
# m2 = re.search("(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)", info[key])
# datetime = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), m2.group('M'),m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S'))
# hand.starttime = time.strptime(time.gmtime(info[key]))
# hand.starttime = time.gmtime(int(info[key]))
hand.starttime = time.gmtime(int(info[key]))
if key == 'HID':
hand.handid = info[key]
if key == 'TABLE':
hand.tablename = info[key]
if key == 'BUTTON':
hand.buttonpos = info[key]
def readButton(self, hand):
m = self.re_Button.search(hand.handText)
if m:
for player in hand.players:
if player[1] == m.group('BUTTON'):
hand.buttonpos = player[0]
break
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'), a.group('CASH'))
def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
if hand.gametype['base'] in ("hold"):
#m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
# r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
# r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
# r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
m = re.search('<ACTION TYPE="HAND_BLINDS" PLAYER=".+" KIND="HAND_BB" VALUE="[.0-9]+"></ACTION>(?P<PREFLOP>.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP")|.+)'
'((?P<FLOP><ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN")|.+))?'
'((?P<TURN><ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER")|.+))?'
'((?P<RIVER><ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER" POT="[.0-9]+">.+(?=<SHOWDOWN NAME="HAND_SHOWDOWN")|.+))?', hand.handText,re.DOTALL)
hand.addStreets(m)
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
#print "DEBUG readCommunityCards:", street, hand.streets.group(street)
boardCards = set([])
if street == 'FLOP':
m = self.re_Card.findall(hand.streets[street])
for card in m:
boardCards.add(self.convertWin2dayCards(card))
else:
m = self.re_BoardLast.search(hand.streets[street])
boardCards.add(self.convertWin2dayCards(m.group('CARD')))
hand.setCommunityCards(street, boardCards)
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):
try:
m = self.re_PostSB.search(hand.handText)
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind
hand.addBlind(None, None, None)
for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.re_PostBoth.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText)
if(m == None):
#Not involved in hand
hand.involved = False
else:
hand.hero = m.group('PNAME')
holeCards = set([])
for card in self.re_Card.finditer(m.group('CARDS')):
holeCards.add(self.convertWin2dayCards(card.group('CARD')))
hand.addHoleCards(holeCards, m.group('PNAME'))
def convertWin2dayCards(self, card):
card = int(card)
retCard = ''
cardconvert = { 1:'A',
10:'T',
11:'J',
12:'Q',
13:'K'}
realNumber = card % 13 + 1
if(realNumber in cardconvert):
retCard += cardconvert[realNumber]
else:
retCard += str(realNumber)
if(card > 38):
retCard += 's'
elif(card > 25):
retCard += 'h'
elif(card > 12):
retCard += 'c'
else:
retCard += 'd'
return(retCard)
def readDrawCards(self, hand, street):
logging.debug("readDrawCards")
m = self.re_HeroCards.finditer(hand.streets[street])
if m == None:
hand.involved = False
else:
for player in m:
hand.hero = player.group('PNAME') # Only really need to do this once
newcards = player.group('NEWCARDS')
oldcards = player.group('OLDCARDS')
if newcards == None:
newcards = set()
else:
newcards = set(newcards.split(' '))
if oldcards == None:
oldcards = set()
else:
oldcards = set(oldcards.split(' '))
hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
def readStudPlayerCards(self, hand, street):
# See comments of reference implementation in FullTiltToFpdb.py
logging.debug("readStudPlayerCards")
m = self.re_HeroCards.finditer(hand.streets[street])
for player in m:
#~ logging.debug(player.groupdict())
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
if oldcards:
oldcards = [c.strip() for c in oldcards.split(' ')]
if newcards:
newcards = [c.strip() for c in newcards.split(' ')]
if street=='ANTES':
return
elif street=='THIRD':
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
# hero: [xx][o]
# others: [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
# 4th:
# hero: [xxo] [o]
# others: [o] [o]
# 5th:
# hero: [xxoo] [o]
# others: [oo] [o]
# 6th:
# hero: [xxooo] [o]
# others: [ooo] [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
elif street=='SEVENTH' and newcards:
# hero: [xxoooo] [x]
# others: not reported.
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street])
for action in m:
if action.group('ATYPE') == 'ACTION_RAISE':
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_CALL':
hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_ALLIN':
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_BET':
hand.addBet( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_FOLD':
hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == 'ACTION_CHECK':
hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == 'ACTION_DISCARD':
hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED'))
elif action.group('ATYPE') == 'ACTION_STAND':
hand.addStandsPat( street, action.group('PNAME'))
else:
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
def readShowdownActions(self, hand):
for shows in self.re_ShowdownAction.finditer(hand.handText):
showdownCards = set([])
for card in self.re_Card.finditer(shows.group('CARDS')):
print "DEBUG:", card, card.group('CARD'), self.convertWin2dayCards(card.group('CARD'))
showdownCards.add(self.convertWin2dayCards(card.group('CARD')))
hand.addShownCards(showdownCards, shows.group('PNAME'))
def readCollectPot(self,hand):
for m in self.re_CollectPot.finditer(hand.handText):
potcoll = Decimal(m.group('POT'))
if potcoll > 0:
hand.addCollectPot(player=m.group('PNAME'),pot=potcoll)
def readShownCards(self,hand):
for m in self.re_ShownCards.finditer(hand.handText):
if m.group('CARDS') is not None:
cards = m.group('CARDS')
cards = set(cards.split(' '))
hand.addShownCards(cards=cards, player=m.group('PNAME'))
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 = Win2day(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -19,7 +19,6 @@ import os
import sys import sys
import Options import Options
import string import string
cl_options = string.join(sys.argv[1:]) cl_options = string.join(sys.argv[1:])
(options, sys.argv) = Options.fpdb_options() (options, sys.argv) = Options.fpdb_options()
@ -28,6 +27,8 @@ if not options.errorsToConsole:
errorFile = open('fpdb-error-log.txt', 'w', 0) errorFile = open('fpdb-error-log.txt', 'w', 0)
sys.stderr = errorFile sys.stderr = errorFile
import logging
import pygtk import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk

View File

@ -18,20 +18,21 @@
import os import os
import re import re
import sys import sys
import logging
from time import time, strftime from time import time, strftime
import fpdb_simple import fpdb_simple
import FpdbSQLQueries import FpdbSQLQueries
class fpdb_db: class fpdb_db:
MYSQL_INNODB = 2
PGSQL = 3
SQLITE = 4
def __init__(self): def __init__(self):
"""Simple constructor, doesnt really do anything""" """Simple constructor, doesnt really do anything"""
self.db = None self.db = None
self.cursor = None self.cursor = None
self.sql = {} self.sql = {}
self.MYSQL_INNODB = 2
self.PGSQL = 3
self.SQLITE = 4
# Data Structures for index and foreign key creation # Data Structures for index and foreign key creation
# drop_code is an int with possible values: 0 - don't drop for bulk import # drop_code is an int with possible values: 0 - don't drop for bulk import
@ -72,6 +73,8 @@ class fpdb_db:
, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0} , {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0}
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0} , {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
] ]
, [ # indexes for sqlite (list index 4)
]
] ]
self.foreignKeys = [ self.foreignKeys = [
@ -146,12 +149,12 @@ class fpdb_db:
self.settings = {} self.settings = {}
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows" self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
self.settings.update(config.get_db_parameters()) db = config.get_db_parameters()
self.connect(self.settings['db-backend'], self.connect(backend=db['db-backend'],
self.settings['db-host'], host=db['db-host'],
self.settings['db-databaseName'], database=db['db-databaseName'],
self.settings['db-user'], user=db['db-user'],
self.settings['db-password']) password=db['db-password'])
#end def do_connect #end def do_connect
def connect(self, backend=None, host=None, database=None, def connect(self, backend=None, host=None, database=None,
@ -164,13 +167,13 @@ class fpdb_db:
self.user=user self.user=user
self.password=password self.password=password
self.database=database self.database=database
if backend==self.MYSQL_INNODB: if backend==fpdb_db.MYSQL_INNODB:
import MySQLdb import MySQLdb
try: try:
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True) self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
except: except:
raise fpdb_simple.FpdbError("MySQL connection failed") raise fpdb_simple.FpdbError("MySQL connection failed")
elif backend==self.PGSQL: elif backend==fpdb_db.PGSQL:
import psycopg2 import psycopg2
import psycopg2.extensions import psycopg2.extensions
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
@ -201,12 +204,19 @@ class fpdb_db:
msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user) msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user)
print msg print msg
raise fpdb_simple.FpdbError(msg) raise fpdb_simple.FpdbError(msg)
elif backend==fpdb_db.SQLITE:
logging.info("Connecting to SQLite:%(database)s" % {'database':database})
import sqlite3
self.db = sqlite3.connect(database,detect_types=sqlite3.PARSE_DECLTYPES)
sqlite3.register_converter("bool", lambda x: bool(int(x)))
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
else: else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor() self.cursor=self.db.cursor()
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
# Set up query dictionary as early in the connection process as we can. # Set up query dictionary as early in the connection process as we can.
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name()) self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
self.cursor.execute(self.sql.query['set tx level'])
self.wrongDbVersion=False self.wrongDbVersion=False
try: try:
self.cursor.execute("SELECT * FROM Settings") self.cursor.execute("SELECT * FROM Settings")
@ -237,7 +247,9 @@ class fpdb_db:
def create_tables(self): def create_tables(self):
#todo: should detect and fail gracefully if tables already exist. #todo: should detect and fail gracefully if tables already exist.
logging.debug(self.sql.query['createSettingsTable'])
self.cursor.execute(self.sql.query['createSettingsTable']) self.cursor.execute(self.sql.query['createSettingsTable'])
logging.debug(self.sql.query['createSitesTable'])
self.cursor.execute(self.sql.query['createSitesTable']) self.cursor.execute(self.sql.query['createSitesTable'])
self.cursor.execute(self.sql.query['createGametypesTable']) self.cursor.execute(self.sql.query['createGametypesTable'])
self.cursor.execute(self.sql.query['createPlayersTable']) self.cursor.execute(self.sql.query['createPlayersTable'])
@ -274,8 +286,10 @@ class fpdb_db:
for table in tables: for table in tables:
self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade') self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
elif(self.get_backend_name() == 'SQLite'): elif(self.get_backend_name() == 'SQLite'):
#todo: sqlite version here self.cursor.execute(self.sql.query['list_tables'])
print "Empty function here" for table in self.cursor.fetchall():
logging.debug(self.sql.query['drop_table'] + table[0])
self.cursor.execute(self.sql.query['drop_table'] + table[0])
self.db.commit() self.db.commit()
#end def drop_tables #end def drop_tables
@ -306,6 +320,8 @@ class fpdb_db:
return "MySQL InnoDB" return "MySQL InnoDB"
elif self.backend==3: elif self.backend==3:
return "PostgreSQL" return "PostgreSQL"
elif self.backend==4:
return "SQLite"
else: else:
raise fpdb_simple.FpdbError("invalid backend") raise fpdb_simple.FpdbError("invalid backend")
#end def get_backend_name #end def get_backend_name
@ -315,11 +331,15 @@ class fpdb_db:
#end def get_db_info #end def get_db_info
def fillDefaultData(self): def fillDefaultData(self):
self.cursor.execute("INSERT INTO Settings VALUES (118);") self.cursor.execute("INSERT INTO Settings (version) VALUES (118);")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');") self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('Full Tilt Poker', 'USD')")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');") self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');") self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('Everleaf', 'USD')")
self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('Win2day', 'USD')")
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);") self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
#self.cursor.execute("""INSERT INTO TourneyTypes
# (siteId,buyin,fee,knockout,rebuyOrAddon) VALUES
# (1,0,0,0,?)""",(False,) )
#end def fillDefaultData #end def fillDefaultData
def recreate_tables(self): def recreate_tables(self):
@ -617,7 +637,7 @@ class fpdb_db:
ret = -1 ret = -1
else: else:
ret = row[0] ret = row[0]
elif self.backend == self.SQLITE: elif self.backend == fpdb_db.SQLITE:
# don't know how to do this in sqlite # don't know how to do this in sqlite
print "getLastInsertId(): not coded for sqlite yet" print "getLastInsertId(): not coded for sqlite yet"
ret = -1 ret = -1
@ -628,26 +648,69 @@ class fpdb_db:
def storeHand(self, p): def storeHand(self, p):
#stores into table hands: #stores into table hands:
self.cursor.execute ("""INSERT INTO Hands self.cursor.execute ("""INSERT INTO Hands (
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats tablename,
,boardcard1, boardcard2, boardcard3, boardcard4, boardcard5 sitehandno,
,playersVpi, playersAtStreet1, playersAtStreet2 gametypeid,
,playersAtStreet3, playersAtStreet4, playersAtShowdown handstart,
,street0Raises, street1Raises, street2Raises importtime,
,street3Raises, street4Raises, street1Pot seats,
,street2Pot, street3Pot, street4Pot maxseats,
,showdownPot boardcard1,
boardcard2,
boardcard3,
boardcard4,
boardcard5,
-- texture,
playersVpi,
playersAtStreet1,
playersAtStreet2,
playersAtStreet3,
playersAtStreet4,
playersAtShowdown,
street0Raises,
street1Raises,
street2Raises,
street3Raises,
street4Raises,
-- street1Pot,
-- street2Pot,
-- street3Pot,
-- street4Pot,
-- showdownPot
) )
VALUES VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats'] %s, %s, %s, %s, %s, %s, %s)""",
,p['boardcard1'], ['boardcard2'], p['boardcard3'], ['boardcard4'], ['boardcard5'] (
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2'] p['tablename'],
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown'] p['sitehandno'],
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises'] p['gametypeid'],
,hudCache['street3Raises'], hudCache['street4Raises'], hudCache['street1Pot'] p['handStart'],
,hudCache['street2Pot'], hudCache['street3Pot'], hudCache['street4Pot'] datetime.datetime.today(),
,hudCache['showdownPot'] len(p['names']),
p['maxSeats'],
p['boardcard1'],
p['boardcard2'],
p['boardcard3'],
p['boardcard4'],
p['boardcard5'],
hudCache['playersVpi'],
hudCache['playersAtStreet1'],
hudCache['playersAtStreet2'],
hudCache['playersAtStreet3'],
hudCache['playersAtStreet4'],
hudCache['playersAtShowdown'],
hudCache['street0Raises'],
hudCache['street1Raises'],
hudCache['street2Raises'],
hudCache['street3Raises'],
hudCache['street4Raises'],
hudCache['street1Pot'],
hudCache['street2Pot'],
hudCache['street3Pot'],
hudCache['street4Pot'],
hudCache['showdownPot']
) )
) )
#return getLastInsertId(backend, conn, cursor) #return getLastInsertId(backend, conn, cursor)

View File

@ -22,6 +22,7 @@
import os # todo: remove this once import_dir is in fpdb_import import os # todo: remove this once import_dir is in fpdb_import
import sys import sys
from time import time, strftime from time import time, strftime
import logging
import traceback import traceback
import math import math
import datetime import datetime