Merge branch 'master' of git://repo.or.cz/fpbd-bostik

This commit is contained in:
Worros 2008-12-07 14:17:05 +09:00
commit 425997d66a
4 changed files with 426 additions and 401 deletions

View File

@ -577,7 +577,7 @@ class FpdbSQLQueries:
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)""" self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax # FIXME: This query has a different syntax
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)""" self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """ self.query['addHandsIndex'] = """ """
@ -585,7 +585,7 @@ class FpdbSQLQueries:
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)""" self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax # FIXME: This query has a different syntax
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)""" self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """ self.query['addHandsIndex'] = """ """
@ -593,9 +593,9 @@ class FpdbSQLQueries:
self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)""" self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax # FIXME: This query has a different syntax
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)""" self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """ self.query['addPlayersIndex'] = """ """
################################ ################################
# Queries used in GuiGraphViewer # Queries used in GuiGraphViewer

View File

@ -118,12 +118,12 @@ class fpdb:
def dia_create_del_database(self, widget, data): def dia_create_del_database(self, widget, data):
print "todo: implement dia_create_del_database" print "todo: implement dia_create_del_database"
obtain_global_lock() self.obtain_global_lock()
#end def dia_create_del_database #end def dia_create_del_database
def dia_create_del_user(self, widget, data): def dia_create_del_user(self, widget, data):
print "todo: implement dia_create_del_user" print "todo: implement dia_create_del_user"
obtain_global_lock() self.obtain_global_lock()
#end def dia_create_del_user #end def dia_create_del_user
def dia_database_stats(self, widget, data): def dia_database_stats(self, widget, data):
@ -133,17 +133,17 @@ class fpdb:
def dia_delete_db_parts(self, widget, data): def dia_delete_db_parts(self, widget, data):
print "todo: implement dia_delete_db_parts" print "todo: implement dia_delete_db_parts"
obtain_global_lock() self.obtain_global_lock()
#end def dia_delete_db_parts #end def dia_delete_db_parts
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None): def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
print "todo: implement dia_edit_profile" print "todo: implement dia_edit_profile"
obtain_global_lock() self.obtain_global_lock()
#end def dia_edit_profile #end def dia_edit_profile
def dia_export_db(self, widget, data): def dia_export_db(self, widget, data):
print "todo: implement dia_export_db" print "todo: implement dia_export_db"
obtain_global_lock() self.obtain_global_lock()
#end def dia_export_db #end def dia_export_db
def dia_get_db_root_credentials(self): def dia_get_db_root_credentials(self):
@ -168,7 +168,7 @@ class fpdb:
def dia_import_db(self, widget, data): def dia_import_db(self, widget, data):
print "todo: implement dia_import_db" print "todo: implement dia_import_db"
obtain_global_lock() self.obtain_global_lock()
#end def dia_import_db #end def dia_import_db
def dia_licensing(self, widget, data): def dia_licensing(self, widget, data):
@ -264,7 +264,11 @@ class fpdb:
self.db = fpdb_db.fpdb_db() self.db = fpdb_db.fpdb_db()
#print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName'] #print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
self.db.connect(self.settings['db-backend'], self.settings['db-host'], self.settings['db-databaseName'], self.settings['db-user'], self.settings['db-password']) self.db.connect(self.settings['db-backend'],
self.settings['db-host'],
self.settings['db-databaseName'],
self.settings['db-user'],
self.settings['db-password'])
if self.db.wrongDbVersion: if self.db.wrongDbVersion:
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))

View File

@ -21,156 +21,170 @@ import fpdb_simple
import FpdbSQLQueries import FpdbSQLQueries
class fpdb_db: class fpdb_db:
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.MYSQL_INNODB=2
self.PGSQL=3 self.PGSQL=3
self.SQLITE=4 self.SQLITE=4
#end def __init__ #end def __init__
def connect(self, backend, host, database, user, password): def connect(self, backend=None, host=None, database=None,
"""Connects a database with the given parameters""" user=None, password=None):
self.backend=backend """Connects a database with the given parameters"""
self.host=host if backend is None:
self.database=database raise FpdbError('Database backend not defined')
self.user=user self.backend=backend
self.password=password self.host=host
if backend==self.MYSQL_INNODB: self.user=user
import MySQLdb self.password=password
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database) self.database=database
elif backend==self.PGSQL: if backend==self.MYSQL_INNODB:
import psycopg2 import MySQLdb
self.db = psycopg2.connect(host = host, user = user, password = password, database = database) self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database)
else: elif backend==self.PGSQL:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) import psycopg2
self.cursor=self.db.cursor() # If DB connection is made over TCP, then the variables
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED') # host, user and password are required
# Set up query dictionary as early in the connection process as we can. if self.host or self.user:
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name()) self.db = psycopg2.connect(host = host,
self.wrongDbVersion=False user = user,
try: password = password,
self.cursor.execute("SELECT * FROM Settings") database = database)
settings=self.cursor.fetchone() # For local domain-socket connections, only DB name is
if settings[0]!=118: # needed, and everything else is in fact undefined and/or
print "outdated or too new database version - please recreate tables" # flat out wrong
self.wrongDbVersion=True else:
except:# _mysql_exceptions.ProgrammingError: self.db = psycopg2.connect(database = database)
print "failed to read settings table - please recreate tables" else:
self.wrongDbVersion=True raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
#end def connect self.cursor=self.db.cursor()
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
# Set up query dictionary as early in the connection process as we can.
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
self.wrongDbVersion=False
try:
self.cursor.execute("SELECT * FROM Settings")
settings=self.cursor.fetchone()
if settings[0]!=118:
print "outdated or too new database version - please recreate tables"
self.wrongDbVersion=True
except:# _mysql_exceptions.ProgrammingError:
print "failed to read settings table - please recreate tables"
self.wrongDbVersion=True
#end def connect
def disconnect(self, due_to_error=False): def disconnect(self, due_to_error=False):
"""Disconnects the DB""" """Disconnects the DB"""
if due_to_error: if due_to_error:
self.db.rollback() self.db.rollback()
else: else:
self.db.commit() self.db.commit()
self.cursor.close() self.cursor.close()
self.db.close() self.db.close()
#end def disconnect #end def disconnect
def reconnect(self, due_to_error=False): def reconnect(self, due_to_error=False):
"""Reconnects the DB""" """Reconnects the DB"""
#print "started fpdb_db.reconnect" #print "started fpdb_db.reconnect"
self.disconnect(due_to_error) self.disconnect(due_to_error)
self.connect(self.backend, self.host, self.database, self.user, self.password) self.connect(self.backend, self.host, self.database, self.user, self.password)
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.
self.cursor.execute(self.sql.query['createSettingsTable']) self.cursor.execute(self.sql.query['createSettingsTable'])
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'])
self.cursor.execute(self.sql.query['createAutoratesTable']) self.cursor.execute(self.sql.query['createAutoratesTable'])
self.cursor.execute(self.sql.query['createHandsTable']) self.cursor.execute(self.sql.query['createHandsTable'])
self.cursor.execute(self.sql.query['createBoardCardsTable']) self.cursor.execute(self.sql.query['createBoardCardsTable'])
self.cursor.execute(self.sql.query['createTourneyTypesTable']) self.cursor.execute(self.sql.query['createTourneyTypesTable'])
self.cursor.execute(self.sql.query['createTourneysTable']) self.cursor.execute(self.sql.query['createTourneysTable'])
self.cursor.execute(self.sql.query['createTourneysPlayersTable']) self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
self.cursor.execute(self.sql.query['createHandsPlayersTable']) self.cursor.execute(self.sql.query['createHandsPlayersTable'])
self.cursor.execute(self.sql.query['createHandsActionsTable']) self.cursor.execute(self.sql.query['createHandsActionsTable'])
self.cursor.execute(self.sql.query['createHudCacheTable']) self.cursor.execute(self.sql.query['createHudCacheTable'])
self.cursor.execute(self.sql.query['addTourneyIndex']) self.cursor.execute(self.sql.query['addTourneyIndex'])
self.cursor.execute(self.sql.query['addHandsIndex']) self.cursor.execute(self.sql.query['addHandsIndex'])
self.cursor.execute(self.sql.query['addPlayersIndex']) self.cursor.execute(self.sql.query['addPlayersIndex'])
self.fillDefaultData() self.fillDefaultData()
self.db.commit() self.db.commit()
#end def disconnect #end def disconnect
def drop_tables(self): def drop_tables(self):
"""Drops the fpdb tables from the current db""" """Drops the fpdb tables from the current db"""
if(self.get_backend_name() == 'MySQL InnoDB'): if(self.get_backend_name() == 'MySQL InnoDB'):
#Databases with FOREIGN KEY support need this switched of before you can drop tables #Databases with FOREIGN KEY support need this switched of before you can drop tables
self.drop_referencial_integrity() self.drop_referencial_integrity()
# Query the DB to see what tables exist # Query the DB to see what tables exist
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
for table in self.cursor: for table in self.cursor:
self.cursor.execute(self.sql.query['drop_table'] + table[0]) self.cursor.execute(self.sql.query['drop_table'] + table[0])
elif(self.get_backend_name() == 'PostgreSQL'): elif(self.get_backend_name() == 'PostgreSQL'):
self.db.commit()# I have no idea why this makes the query work--REB 07OCT2008 self.db.commit()# I have no idea why this makes the query work--REB 07OCT2008
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
tables = self.cursor.fetchall() tables = self.cursor.fetchall()
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 #todo: sqlite version here
print "Empty function here" print "Empty function here"
self.db.commit() self.db.commit()
#end def drop_tables #end def drop_tables
def drop_referencial_integrity(self): def drop_referencial_integrity(self):
"""Update all tables to remove foreign keys""" """Update all tables to remove foreign keys"""
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
result = self.cursor.fetchall() result = self.cursor.fetchall()
for i in range(len(result)): for i in range(len(result)):
self.cursor.execute("SHOW CREATE TABLE " + result[i][0]) self.cursor.execute("SHOW CREATE TABLE " + result[i][0])
inner = self.cursor.fetchall() inner = self.cursor.fetchall()
for j in range(len(inner)): for j in range(len(inner)):
# result[i][0] - Table name # result[i][0] - Table name
# result[i][1] - CREATE TABLE parameters # result[i][1] - CREATE TABLE parameters
#Searching for CONSTRAINT `tablename_ibfk_1` #Searching for CONSTRAINT `tablename_ibfk_1`
for m in re.finditer('(ibfk_[0-9]+)', inner[j][1]): for m in re.finditer('(ibfk_[0-9]+)', inner[j][1]):
key = "`" + inner[j][0] + "_" + m.group() + "`" key = "`" + inner[j][0] + "_" + m.group() + "`"
self.cursor.execute("ALTER TABLE " + inner[j][0] + " DROP FOREIGN KEY " + key) self.cursor.execute("ALTER TABLE " + inner[j][0] + " DROP FOREIGN KEY " + key)
self.db.commit() self.db.commit()
#end drop_referencial_inegrity #end drop_referencial_inegrity
def get_backend_name(self): def get_backend_name(self):
"""Returns the name of the currently used backend""" """Returns the name of the currently used backend"""
if self.backend==2: if self.backend==2:
return "MySQL InnoDB" return "MySQL InnoDB"
elif self.backend==3: elif self.backend==3:
return "PostgreSQL" return "PostgreSQL"
else: else:
raise fpdb_simple.FpdbError("invalid backend") raise fpdb_simple.FpdbError("invalid backend")
#end def get_backend_name #end def get_backend_name
def get_db_info(self): def get_db_info(self):
return (self.host, self.database, self.user, self.password) return (self.host, self.database, self.user, self.password)
#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 VALUES (118);")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');") self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');") self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', '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);")
#end def fillDefaultData #end def fillDefaultData
def recreate_tables(self): def recreate_tables(self):
"""(Re-)creates the tables of the current DB""" """(Re-)creates the tables of the current DB"""
self.drop_tables() self.drop_tables()
self.create_tables() self.create_tables()
self.db.commit() self.db.commit()
print "Finished recreating tables" print "Finished recreating tables"
#end def recreate_tables #end def recreate_tables
#end class fpdb_db #end class fpdb_db

View File

@ -20,16 +20,16 @@
import sys import sys
try: try:
import MySQLdb import MySQLdb
mysqlLibFound=True mysqlLibFound=True
except: except:
pass pass
try: try:
import psycopg2 import psycopg2
pgsqlLibFound=True pgsqlLibFound=True
except: except:
pass pass
import traceback import traceback
import math import math
@ -42,274 +42,281 @@ from time import time
class Importer: class Importer:
def __init__(self, caller, settings, config): def __init__(self, caller, settings, config):
"""Constructor""" """Constructor"""
self.settings=settings self.settings=settings
self.caller=caller self.caller=caller
self.config = config self.config = config
self.db = None self.db = None
self.cursor = None self.cursor = None
self.filelist = {} self.filelist = {}
self.dirlist = {} self.dirlist = {}
self.monitor = False self.monitor = False
self.updated = {} #Time last import was run {file:mtime} self.updated = {} #Time last import was run {file:mtime}
self.lines = None self.lines = None
self.faobs = None #File as one big string self.faobs = None #File as one big string
self.pos_in_file = {} # dict to remember how far we have read in the file self.pos_in_file = {} # dict to remember how far we have read in the file
#Set defaults #Set defaults
self.callHud = self.config.get_import_parameters().get("callFpdbHud") self.callHud = self.config.get_import_parameters().get("callFpdbHud")
if not self.settings.has_key('minPrint'): if not self.settings.has_key('minPrint'):
self.settings['minPrint'] = 30 self.settings['minPrint'] = 30
self.dbConnect() self.dbConnect()
def dbConnect(self): # XXX: Why is this here, when fpdb_db.connect() already does the
#connect to DB # same?
if self.settings['db-backend'] == 2: def dbConnect(self):
if not mysqlLibFound: #connect to DB
raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file") if self.settings['db-backend'] == 2:
self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'], if not mysqlLibFound:
self.settings['db-password'], self.settings['db-databaseName']) raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file")
elif self.settings['db-backend'] == 3: self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'],
if not pgsqlLibFound: self.settings['db-password'], self.settings['db-databaseName'])
raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file") elif self.settings['db-backend'] == 3:
print self.settings if not pgsqlLibFound:
self.db = psycopg2.connect(host = self.settings['db-host'], raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file")
user = self.settings['db-user'], print self.settings
password = self.settings['db-password'], if not self.settings.has_key('db-host') or \
database = self.settings['db-databaseName']) not self.settings.has_key('db-user'):
elif self.settings['db-backend'] == 4: self.db = psycopg2.connect(host = self.settings['db-host'],
pass user = self.settings['db-user'],
else: password = self.settings['db-password'],
pass database = self.settings['db-databaseName'])
self.cursor = self.db.cursor() else:
dbname = self.settings['db-databaseName']
self.db = psycopg2.connect(database = dbname)
elif self.settings['db-backend'] == 4:
pass
else:
pass
self.cursor = self.db.cursor()
#Set functions #Set functions
def setCallHud(self, value): def setCallHud(self, value):
self.callHud = value self.callHud = value
def setMinPrint(self, value): def setMinPrint(self, value):
self.settings['minPrint'] = int(value) self.settings['minPrint'] = int(value)
def setHandCount(self, value): def setHandCount(self, value):
self.settings['handCount'] = int(value) self.settings['handCount'] = int(value)
def setQuiet(self, value): def setQuiet(self, value):
self.settings['quiet'] = value self.settings['quiet'] = value
def setFailOnError(self, value): def setFailOnError(self, value):
self.settings['failOnError'] = value self.settings['failOnError'] = value
# def setWatchTime(self): # def setWatchTime(self):
# self.updated = time() # self.updated = time()
def clearFileList(self): def clearFileList(self):
self.filelist = {} self.filelist = {}
#Add an individual file to filelist #Add an individual file to filelist
def addImportFile(self, filename, site = "default", filter = "passthrough"): def addImportFile(self, filename, site = "default", filter = "passthrough"):
#TODO: test it is a valid file #TODO: test it is a valid file
self.filelist[filename] = [site] + [filter] self.filelist[filename] = [site] + [filter]
#Add a directory of files to filelist #Add a directory of files to filelist
#Only one import directory per site supported. #Only one import directory per site supported.
#dirlist is a hash of lists: #dirlist is a hash of lists:
#dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"): def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
if os.path.isdir(dir): if os.path.isdir(dir):
if monitor == True: if monitor == True:
self.monitor = True self.monitor = True
self.dirlist[site] = [dir] + [filter] self.dirlist[site] = [dir] + [filter]
for file in os.listdir(dir): for file in os.listdir(dir):
self.addImportFile(os.path.join(dir, file), site, filter) self.addImportFile(os.path.join(dir, file), site, filter)
else: else:
print "Warning: Attempted to add: '" + str(dir) + "' as an import directory" print "Warning: Attempted to add: '" + str(dir) + "' as an import directory"
#Run full import on filelist #Run full import on filelist
def runImport(self): def runImport(self):
for file in self.filelist: for file in self.filelist:
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
#Run import on updated files, then store latest update time. #Run import on updated files, then store latest update time.
def runUpdated(self): def runUpdated(self):
#Check for new files in directory #Check for new files in directory
#todo: make efficient - always checks for new file, should be able to use mtime of directory #todo: make efficient - always checks for new file, should be able to use mtime of directory
# ^^ May not work on windows # ^^ May not work on windows
for site in self.dirlist: for site in self.dirlist:
self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1]) self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
for file in self.filelist: for file in self.filelist:
stat_info = os.stat(file) stat_info = os.stat(file)
try: try:
lastupdate = self.updated[file] lastupdate = self.updated[file]
if stat_info.st_mtime > lastupdate: if stat_info.st_mtime > lastupdate:
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
self.updated[file] = time() self.updated[file] = time()
except: except:
self.updated[file] = time() self.updated[file] = time()
# This codepath only runs first time the file is found, if modified in the last # This codepath only runs first time the file is found, if modified in the last
# minute run an immediate import. # minute run an immediate import.
if (time() - stat_info.st_mtime) < 60: if (time() - stat_info.st_mtime) < 60:
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
# This is now an internal function that should not be called directly. # This is now an internal function that should not be called directly.
def import_file_dict(self, file, site, filter): def import_file_dict(self, file, site, filter):
if(filter == "passthrough"): if(filter == "passthrough"):
self.import_fpdb_file(file, site) self.import_fpdb_file(file, site)
else: else:
#Load filter, and run filtered file though main importer #Load filter, and run filtered file though main importer
self.import_fpdb_file(file, site) self.import_fpdb_file(file, site)
def import_fpdb_file(self, file, site): def import_fpdb_file(self, file, site):
starttime = time() starttime = time()
last_read_hand=0 last_read_hand=0
loc = 0 loc = 0
if (file=="stdin"): if (file=="stdin"):
inputFile=sys.stdin inputFile=sys.stdin
else: else:
inputFile=open(file, "rU") inputFile=open(file, "rU")
try: loc = self.pos_in_file[file] try: loc = self.pos_in_file[file]
except: pass except: pass
# Read input file into class and close file # Read input file into class and close file
inputFile.seek(loc) inputFile.seek(loc)
self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines()) self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines())
self.pos_in_file[file] = inputFile.tell() self.pos_in_file[file] = inputFile.tell()
inputFile.close() inputFile.close()
try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return. try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
firstline = self.lines[0] firstline = self.lines[0]
except: except:
# print "import_fpdb_file", file, site, self.lines, "\n" # print "import_fpdb_file", file, site, self.lines, "\n"
return return
if firstline.find("Tournament Summary")!=-1: if firstline.find("Tournament Summary")!=-1:
print "TODO: implement importing tournament summaries" print "TODO: implement importing tournament summaries"
#self.faobs = readfile(inputFile) #self.faobs = readfile(inputFile)
#self.parseTourneyHistory() #self.parseTourneyHistory()
return 0 return 0
site=fpdb_simple.recogniseSite(firstline) site=fpdb_simple.recogniseSite(firstline)
category=fpdb_simple.recogniseCategory(firstline) category=fpdb_simple.recogniseCategory(firstline)
startpos=0 startpos=0
stored=0 #counter stored=0 #counter
duplicates=0 #counter duplicates=0 #counter
partial=0 #counter partial=0 #counter
errors=0 #counter errors=0 #counter
for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method
if (len(self.lines[i])<2): if (len(self.lines[i])<2):
endpos=i endpos=i
hand=self.lines[startpos:endpos] hand=self.lines[startpos:endpos]
if (len(hand[0])<2): if (len(hand[0])<2):
hand=hand[1:] hand=hand[1:]
cancelled=False cancelled=False
damaged=False damaged=False
if (site=="ftp"): if (site=="ftp"):
for i in range (len(hand)): for i in range (len(hand)):
if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right? if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right?
cancelled=True cancelled=True
seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line
if (seat1!=-1): if (seat1!=-1):
if (hand[i].find("Seat ", seat1+3)!=-1): if (hand[i].find("Seat ", seat1+3)!=-1):
damaged=True damaged=True
if (len(hand)<3): if (len(hand)<3):
pass pass
#todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work. #todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work.
elif (hand[0].endswith(" (partial)")): #partial hand - do nothing elif (hand[0].endswith(" (partial)")): #partial hand - do nothing
partial+=1 partial+=1
elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and? elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and?
partial+=1 partial+=1
elif (cancelled or damaged): elif (cancelled or damaged):
partial+=1 partial+=1
else: #normal processing else: #normal processing
isTourney=fpdb_simple.isTourney(hand[0]) isTourney=fpdb_simple.isTourney(hand[0])
if not isTourney: if not isTourney:
fpdb_simple.filterAnteBlindFold(site,hand) fpdb_simple.filterAnteBlindFold(site,hand)
hand=fpdb_simple.filterCrap(site, hand, isTourney) hand=fpdb_simple.filterCrap(site, hand, isTourney)
self.hand=hand self.hand=hand
try: try:
handsId=fpdb_parse_logic.mainParser(self.db, self.cursor, site, category, hand) handsId=fpdb_parse_logic.mainParser(self.db, self.cursor, site, category, hand)
self.db.commit() self.db.commit()
stored+=1 stored+=1
self.db.commit() self.db.commit()
if self.callHud: if self.callHud:
#print "call to HUD here. handsId:",handsId #print "call to HUD here. handsId:",handsId
#pipe the Hands.id out to the HUD #pipe the Hands.id out to the HUD
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
except fpdb_simple.DuplicateError: except fpdb_simple.DuplicateError:
duplicates+=1 duplicates+=1
except (ValueError), fe: except (ValueError), fe:
errors+=1 errors+=1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.db.commit() #dont remove this, in case hand processing was cancelled. self.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
except (fpdb_simple.FpdbError), fe: except (fpdb_simple.FpdbError), fe:
errors+=1 errors+=1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
#fe.printStackTrace() #todo: get stacktrace #fe.printStackTrace() #todo: get stacktrace
self.db.rollback() self.db.rollback()
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.db.commit() #dont remove this, in case hand processing was cancelled. self.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
if (self.settings['minPrint']!=0): if (self.settings['minPrint']!=0):
if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0): if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0):
print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors
if (self.settings['handCount']!=0): if (self.settings['handCount']!=0):
if ((stored+duplicates+partial+errors)>=self.settings['handCount']): if ((stored+duplicates+partial+errors)>=self.settings['handCount']):
if (not self.settings['quiet']): if (not self.settings['quiet']):
print "quitting due to reaching the amount of hands to be imported" print "quitting due to reaching the amount of hands to be imported"
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime) print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime)
sys.exit(0) sys.exit(0)
startpos=endpos startpos=endpos
print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime) print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime)
if stored==0: if stored==0:
if duplicates>0: if duplicates>0:
for line_no in range(len(self.lines)): for line_no in range(len(self.lines)):
if self.lines[line_no].find("Game #")!=-1: if self.lines[line_no].find("Game #")!=-1:
final_game_line=self.lines[line_no] final_game_line=self.lines[line_no]
handsId=fpdb_simple.parseSiteHandNo(final_game_line) handsId=fpdb_simple.parseSiteHandNo(final_game_line)
else: else:
print "failed to read a single hand from file:", inputFile print "failed to read a single hand from file:", inputFile
handsId=0 handsId=0
#todo: this will cause return of an unstored hand number if the last hand was error or partial #todo: this will cause return of an unstored hand number if the last hand was error or partial
self.db.commit() self.db.commit()
self.handsId=handsId self.handsId=handsId
return handsId return handsId
#end def import_file_dict #end def import_file_dict
def parseTourneyHistory(self): def parseTourneyHistory(self):
print "Tourney history parser stub" print "Tourney history parser stub"
#Find tournament boundaries. #Find tournament boundaries.
#print self.foabs #print self.foabs
def printEmailErrorMessage(self, errors, filename, line): def printEmailErrorMessage(self, errors, filename, line):
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it." print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
print "Filename:", filename print "Filename:", filename
print "Here is the first line so you can identify it. Please mention that the error was a ValueError:" print "Here is the first line so you can identify it. Please mention that the error was a ValueError:"
print self.hand[0] print self.hand[0]
print "Hand logged to hand-errors.txt" print "Hand logged to hand-errors.txt"
logfile = open('hand-errors.txt', 'a') logfile = open('hand-errors.txt', 'a')
for s in self.hand: for s in self.hand:
logfile.write(str(s) + "\n") logfile.write(str(s) + "\n")
logfile.write("\n") logfile.write("\n")
logfile.close() logfile.close()
if __name__ == "__main__": if __name__ == "__main__":
print "CLI for fpdb_import is now available as CliFpdb.py" print "CLI for fpdb_import is now available as CliFpdb.py"