diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 36fdd2d2..c5cd156a 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -187,6 +187,7 @@ class Database: def __init__(self, c, sql = None): log.info("Creating Database instance, sql = %s" % sql) self.config = c + self.__connected = False self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql self.do_connect(c) @@ -237,7 +238,12 @@ class Database: self.hud_style = style def do_connect(self, c): - self.fdb.do_connect(c) + try: + self.fdb.do_connect(c) + except: + # error during connect + self.__connected = False + raise self.connection = self.fdb.db self.wrongDbVersion = self.fdb.wrongDbVersion @@ -247,6 +253,7 @@ class Database: self.db_server = db_params['db-server'] self.database = db_params['db-databaseName'] self.host = db_params['db-host'] + self.__connected = True def commit(self): self.fdb.db.commit() @@ -254,6 +261,9 @@ class Database: def rollback(self): self.fdb.db.rollback() + def connected(self): + return self.__connected + def get_cursor(self): return self.connection.cursor() @@ -1608,103 +1618,43 @@ class Database: pdata[p]['street2Bets'], pdata[p]['street3Bets'], pdata[p]['street4Bets'], + pdata[p]['position'], + pdata[p]['tourneyTypeId'], + pdata[p]['startCards'], + pdata[p]['street0_3BChance'], + pdata[p]['street0_3BDone'], + pdata[p]['otherRaisedStreet1'], + pdata[p]['otherRaisedStreet2'], + pdata[p]['otherRaisedStreet3'], + pdata[p]['otherRaisedStreet4'], + pdata[p]['foldToOtherRaisedStreet1'], + pdata[p]['foldToOtherRaisedStreet2'], + pdata[p]['foldToOtherRaisedStreet3'], + pdata[p]['foldToOtherRaisedStreet4'], + pdata[p]['stealAttemptChance'], + pdata[p]['stealAttempted'], + pdata[p]['foldBbToStealChance'], + pdata[p]['foldedBbToSteal'], + pdata[p]['foldSbToStealChance'], + pdata[p]['foldedSbToSteal'], + pdata[p]['foldToStreet1CBChance'], + pdata[p]['foldToStreet1CBDone'], + pdata[p]['foldToStreet2CBChance'], + pdata[p]['foldToStreet2CBDone'], + pdata[p]['foldToStreet3CBChance'], + pdata[p]['foldToStreet3CBDone'], + pdata[p]['foldToStreet4CBChance'], + pdata[p]['foldToStreet4CBDone'], + pdata[p]['street1CheckCallRaiseChance'], + pdata[p]['street1CheckCallRaiseDone'], + pdata[p]['street2CheckCallRaiseChance'], + pdata[p]['street2CheckCallRaiseDone'], + pdata[p]['street3CheckCallRaiseChance'], + pdata[p]['street3CheckCallRaiseDone'], + pdata[p]['street4CheckCallRaiseChance'] ) ) - q = """INSERT INTO HandsPlayers ( - handId, - playerId, - startCash, - seatNo, - card1, - card2, - card3, - card4, - card5, - card6, - card7, - winnings, - rake, - totalProfit, - street0VPI, - street1Seen, - street2Seen, - street3Seen, - street4Seen, - sawShowdown, - wonAtSD, - street0Aggr, - street1Aggr, - street2Aggr, - street3Aggr, - street4Aggr, - street1CBChance, - street2CBChance, - street3CBChance, - street4CBChance, - street1CBDone, - street2CBDone, - street3CBDone, - street4CBDone, - wonWhenSeenStreet1, - street0Calls, - street1Calls, - street2Calls, - street3Calls, - street4Calls, - street0Bets, - street1Bets, - street2Bets, - street3Bets, - street4Bets - ) - 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, %s, - %s, %s, %s, %s, %s - )""" - -# position, -# tourneyTypeId, -# startCards, -# street0_3BChance, -# street0_3BDone, -# otherRaisedStreet1, -# otherRaisedStreet2, -# otherRaisedStreet3, -# otherRaisedStreet4, -# foldToOtherRaisedStreet1, -# foldToOtherRaisedStreet2, -# foldToOtherRaisedStreet3, -# foldToOtherRaisedStreet4, -# stealAttemptChance, -# stealAttempted, -# foldBbToStealChance, -# foldedBbToSteal, -# foldSbToStealChance, -# foldedSbToSteal, -# foldToStreet1CBChance, -# foldToStreet1CBDone, -# foldToStreet2CBChance, -# foldToStreet2CBDone, -# foldToStreet3CBChance, -# foldToStreet3CBDone, -# foldToStreet4CBChance, -# foldToStreet4CBDone, -# street1CheckCallRaiseChance, -# street1CheckCallRaiseDone, -# street2CheckCallRaiseChance, -# street2CheckCallRaiseDone, -# street3CheckCallRaiseChance, -# street3CheckCallRaiseDone, -# street4CheckCallRaiseChance, -# street4CheckCallRaiseDone, - + q = self.sql.query['store_hands_players'] q = q.replace('%s', self.sql.query['placeholder']) #print "DEBUG: inserts: %s" %inserts diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index e4072d8a..1064a600 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -53,6 +53,27 @@ class DerivedStats(): self.handsplayers[player[1]]['street%dCBChance' %i] = False self.handsplayers[player[1]]['street%dCBDone' %i] = False + #FIXME - Everything below this point is incomplete. + self.handsplayers[player[1]]['position'] = 2 + self.handsplayers[player[1]]['tourneyTypeId'] = 1 + self.handsplayers[player[1]]['startCards'] = 0 + self.handsplayers[player[1]]['street0_3BChance'] = False + self.handsplayers[player[1]]['street0_3BDone'] = False + self.handsplayers[player[1]]['stealAttemptChance'] = False + self.handsplayers[player[1]]['stealAttempted'] = False + self.handsplayers[player[1]]['foldBbToStealChance'] = False + self.handsplayers[player[1]]['foldBbToStealChance'] = False + self.handsplayers[player[1]]['foldSbToStealChance'] = False + self.handsplayers[player[1]]['foldedSbToSteal'] = False + self.handsplayers[player[1]]['foldedBbToSteal'] = False + for i in range(1,5): + self.handsplayers[player[1]]['otherRaisedStreet%d' %i] = False + self.handsplayers[player[1]]['foldToOtherRaisedStreet%d' %i] = False + self.handsplayers[player[1]]['foldToStreet%dCBChance' %i] = False + self.handsplayers[player[1]]['foldToStreet%dCBDone' %i] = False + self.handsplayers[player[1]]['street%dCheckCallRaiseChance' %i] = False + self.handsplayers[player[1]]['street%dCheckCallRaiseDone' %i] = False + self.assembleHands(self.hand) self.assembleHandsPlayers(self.hand) @@ -149,6 +170,7 @@ class DerivedStats(): for i, card in enumerate(hcs[:7], 1): self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card) + # position, #Stud 3rd street card test # denny501: brings in for $0.02 @@ -159,16 +181,6 @@ class DerivedStats(): # u.pressure: folds (Seat 1) # 123smoothie: calls $0.02 # gashpor: calls $0.02 - # tourneyTypeId, - # startCards, - # street0_3BChance,street0_3BDone, - # otherRaisedStreet1-4 - # foldToOtherRaisedStreet1-4 - # stealAttemptChance,stealAttempted, - # foldBbToStealChance,foldedBbToSteal, - # foldSbToStealChance,foldedSbToSteal, - # foldToStreet1-4CBChance, foldToStreet1-4CBDone, - # street1-4CheckCallRaiseChance, street1-4CheckCallRaiseDone, # Additional stats # 3betSB, 3betBB diff --git a/pyfpdb/Exceptions.py b/pyfpdb/Exceptions.py index 5b4f4391..fd7c20e3 100644 --- a/pyfpdb/Exceptions.py +++ b/pyfpdb/Exceptions.py @@ -34,5 +34,19 @@ class FpdbMySQLNoDatabase(FpdbDatabaseError): def __str__(self): return repr(self.value +" " + self.errmsg) +class FpdbPostgresqlAccessDenied(FpdbDatabaseError): + def __init__(self, value='', errmsg=''): + self.value = value + self.errmsg = errmsg + def __str__(self): + return repr(self.value +" " + self.errmsg) + +class FpdbPostgresqlNoDatabase(FpdbDatabaseError): + def __init__(self, value='', errmsg=''): + self.value = value + self.errmsg = errmsg + def __str__(self): + return repr(self.value +" " + self.errmsg) + class DuplicateError(FpdbError): pass diff --git a/pyfpdb/GuiLogView.py b/pyfpdb/GuiLogView.py index 3b7b8aa6..29385fa0 100755 --- a/pyfpdb/GuiLogView.py +++ b/pyfpdb/GuiLogView.py @@ -17,7 +17,8 @@ #agpl-3.0.txt in the docs folder of the package. -import mmap +import os +import Queue import pygtk pygtk.require('2.0') @@ -29,18 +30,27 @@ import Configuration log = Configuration.get_logger("logging.conf", "logview") -MAX_LINES = 100000 +MAX_LINES = 100000 # max lines to display in window +EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file +logfile = 'logging.out' # name of logfile class GuiLogView: - def __init__(self, config, mainwin, vbox): + def __init__(self, config, mainwin, closeq): self.config = config self.main_window = mainwin - self.vbox = vbox + self.closeq = closeq + + self.dia = gtk.Dialog(title="Log Messages" + ,parent=None + ,flags=gtk.DIALOG_DESTROY_WITH_PARENT + ,buttons=(gtk.STOCK_CLOSE,gtk.RESPONSE_OK)) + self.dia.set_modal(False) + + self.vbox = self.dia.vbox gtk.Widget.set_size_request(self.vbox, 700, 400); self.liststore = gtk.ListStore(str, str, str, str, gobject.TYPE_BOOLEAN) # date, module, level, text - # this is how to add a filter: # # # Creation of the filter, from the model @@ -49,7 +59,6 @@ class GuiLogView: # # # The TreeView gets the filter as model # self.listview = gtk.TreeView(filter) - self.listview = gtk.TreeView(model=self.liststore) self.listview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_NONE) self.listcols = [] @@ -67,6 +76,7 @@ class GuiLogView: self.listview.show() scrolledwindow.show() self.vbox.show() + self.dia.set_focus(self.listview) col = self.addColumn("Date/Time", 0) col = self.addColumn("Module", 1) @@ -75,6 +85,17 @@ class GuiLogView: self.loadLog() self.vbox.show_all() + self.dia.show() + + self.dia.connect('response', self.dialog_response_cb) + + def dialog_response_cb(self, dialog, response_id): + # this is called whether close button is pressed or window is closed + self.closeq.put(self.__class__) + dialog.destroy() + + def get_dialog(self): + return self.dia def addColumn(self, title, n): col = gtk.TreeViewColumn(title) @@ -92,39 +113,28 @@ class GuiLogView: def loadLog(self): - #self.configStore = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_STRING) - #self.configView = gtk.TreeView(self.configStore) - #self.configView.set_enable_tree_lines(True) self.liststore.clear() self.listcols = [] - # count number of lines in file - f = open('logging.out', "r+") - buf = mmap.mmap(f.fileno(), 0) - readline = buf.readline - lines = 0 - while readline(): - lines += 1 - f.close() + # guesstimate number of lines in file + if os.path.exists(logfile): + stat_info = os.stat(logfile) + lines = stat_info.st_size / EST_CHARS_PER_LINE + print "logview: size =", stat_info.st_size, "lines =", lines - startline = 0 - if lines > MAX_LINES: - # only display from startline if log file is large - startline = lines - MAX_LINES + # set startline to line number to start display from + startline = 0 + if lines > MAX_LINES: + # only display from startline if log file is large + startline = lines - MAX_LINES - f = open('logging.out', "r+") - buf = mmap.mmap(f.fileno(), 0) - readline = buf.readline - l = 0 - line = readline() - while line: - # eg line: - # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised - l = l + 1 - if l > startline and len(line) > 49: - iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) ) - line = readline() - f.close() + l = 0 + for line in open(logfile): + # eg line: + # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised + l = l + 1 + if l > startline and len(line) > 49: + iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) ) def sortCols(self, col, n): try: diff --git a/pyfpdb/GuiPrefs.py b/pyfpdb/GuiPrefs.py index ada9cb51..b85b3f6a 100755 --- a/pyfpdb/GuiPrefs.py +++ b/pyfpdb/GuiPrefs.py @@ -91,10 +91,15 @@ class GuiPrefs: #iter = self.configStore.append( parent, [node.nodeValue, None] ) iter = None if node.nodeType != node.TEXT_NODE and node.nodeType != node.COMMENT_NODE: + name = "" iter = self.configStore.append( parent, [node, setting, value] ) if node.hasAttributes(): for i in xrange(node.attributes.length): self.configStore.append( iter, [node, node.attributes.item(i).localName, node.attributes.item(i).value] ) + if node.attributes.item(i).localName in ('site_name', 'game_name', 'stat_name', 'name', 'db_server', 'site'): + name = " " + node.attributes.item(i).value + if name != "": + self.configStore.set_value(iter, 1, setting+name) if node.hasChildNodes(): for elem in node.childNodes: self.addTreeRows(iter, elem) @@ -156,7 +161,7 @@ if __name__=="__main__": gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)) - dia.set_default_size(500, 500) + dia.set_default_size(700, 500) prefs = GuiPrefs(config, win, dia.vbox) response = dia.run() if response == gtk.RESPONSE_ACCEPT: diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 9ff78249..6901340e 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -55,6 +55,8 @@ class Hand(object): self.handText = handText self.handid = 0 self.dbid_hands = 0 + self.dbid_pids = None + self.dbid_gt = 0 self.tablename = "" self.hero = "" self.maxseats = None @@ -189,22 +191,21 @@ dealt whether they were seen in a 'dealt to' line self.holecards[street][player] = [open, closed] def prepInsert(self, db): - pass + ##### + # Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables + # These functions are intended for prep insert eventually + ##### + # Players - base playerid and siteid tuple + self.dbid_pids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) + + #Gametypes + self.dbid_gt = db.getGameTypeId(self.siteId, self.gametype) def insert(self, db): """ Function to insert Hand into database Should not commit, and do minimal selects. Callers may want to cache commits db: a connected fpdb_db object""" - ##### - # Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables - # These functions are intended for prep insert eventually - ##### - # Players - base playerid and siteid tuple - sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) - - #Gametypes - gtid = db.getGameTypeId(self.siteId, self.gametype) self.stats.getStats(self) @@ -213,14 +214,14 @@ db: a connected fpdb_db object""" ##### hh = self.stats.getHands() - if not db.isDuplicate(gtid, hh['siteHandNo']): + if not db.isDuplicate(self.dbid_gt, hh['siteHandNo']): # Hands - Summary information of hand indexed by handId - gameinfo - hh['gameTypeId'] = gtid + hh['gameTypeId'] = self.dbid_gt # seats TINYINT NOT NULL, - hh['seats'] = len(sqlids) + hh['seats'] = len(self.dbid_pids) self.dbid_hands = db.storeHand(hh) - db.storeHandsPlayers(self.dbid_hands, sqlids, self.stats.getHandsPlayers()) + db.storeHandsPlayers(self.dbid_hands, self.dbid_pids, self.stats.getHandsPlayers()) # HandsActions - all actions for all players for all streets - self.actions # HudCache data can be generated from HandsActions (HandsPlayers?) # Tourneys ? diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 5a0d1965..73d380c0 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -3327,8 +3327,105 @@ class Sql: %s, %s, %s, %s, %s, %s, %s, %s, %s)""" - - + self.query['store_hands_players'] = """INSERT INTO HandsPlayers ( + handId, + playerId, + startCash, + seatNo, + card1, + card2, + card3, + card4, + card5, + card6, + card7, + winnings, + rake, + totalProfit, + street0VPI, + street1Seen, + street2Seen, + street3Seen, + street4Seen, + sawShowdown, + wonAtSD, + street0Aggr, + street1Aggr, + street2Aggr, + street3Aggr, + street4Aggr, + street1CBChance, + street2CBChance, + street3CBChance, + street4CBChance, + street1CBDone, + street2CBDone, + street3CBDone, + street4CBDone, + wonWhenSeenStreet1, + street0Calls, + street1Calls, + street2Calls, + street3Calls, + street4Calls, + street0Bets, + street1Bets, + street2Bets, + street3Bets, + street4Bets, + position, + tourneyTypeId, + startCards, + street0_3BChance, + street0_3BDone, + otherRaisedStreet1, + otherRaisedStreet2, + otherRaisedStreet3, + otherRaisedStreet4, + foldToOtherRaisedStreet1, + foldToOtherRaisedStreet2, + foldToOtherRaisedStreet3, + foldToOtherRaisedStreet4, + stealAttemptChance, + stealAttempted, + foldBbToStealChance, + foldedBbToSteal, + foldSbToStealChance, + foldedSbToSteal, + foldToStreet1CBChance, + foldToStreet1CBDone, + foldToStreet2CBChance, + foldToStreet2CBDone, + foldToStreet3CBChance, + foldToStreet3CBDone, + foldToStreet4CBChance, + foldToStreet4CBDone, + street1CheckCallRaiseChance, + street1CheckCallRaiseDone, + street2CheckCallRaiseChance, + street2CheckCallRaiseDone, + street3CheckCallRaiseChance, + street3CheckCallRaiseDone, + street4CheckCallRaiseChance + ) + 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, + %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, %s + )""" if db_server == 'mysql': self.query['placeholder'] = u'%s' diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index e4c5f22d..bff044d5 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -18,6 +18,7 @@ import os import sys import re +import Queue # if path is set to use an old version of python look for a new one: # (does this work in linux?) @@ -73,6 +74,7 @@ try: import pygtk pygtk.require('2.0') import gtk + import pango except: print "Unable to load PYGTK modules required for GUI. Please install PyCairo, PyGObject, and PyGTK from www.pygtk.org." raw_input("Press ENTER to continue.") @@ -80,6 +82,24 @@ except: import interlocks +# these imports not required in this module, imported here to report version in About dialog +try: + import matplotlib + matplotlib_version = matplotlib.__version__ +except: + matplotlib_version = 'not found' +try: + import numpy + numpy_version = numpy.__version__ +except: + numpy_version = 'not found' +try: + import sqlite3 + sqlite3_version = sqlite3.version + sqlite_version = sqlite3.sqlite_version +except: + sqlite3_version = 'not found' + sqlite_version = 'not found' import GuiPrefs import GuiLogView @@ -112,12 +132,12 @@ class fpdb: def add_tab(self, new_page, new_tab_name): """adds a tab, namely creates the button and displays it and appends all the relevant arrays""" - for name in self.nb_tabs: #todo: check this is valid + for name in self.nb_tab_names: #todo: check this is valid if name == new_tab_name: return # if tab already exists, just go to it used_before = False - for i, name in enumerate(self.tab_names): #todo: check this is valid + for i, name in enumerate(self.tab_names): if name == new_tab_name: used_before = True event_box = self.tabs[i] @@ -133,13 +153,13 @@ class fpdb: #self.nb.append_page(new_page, gtk.Label(new_tab_name)) self.nb.append_page(page, event_box) - self.nb_tabs.append(new_tab_name) + self.nb_tab_names.append(new_tab_name) page.show() def display_tab(self, new_tab_name): """displays the indicated tab""" tab_no = -1 - for i, name in enumerate(self.nb_tabs): + for i, name in enumerate(self.nb_tab_names): if new_tab_name == name: tab_no = i break @@ -190,13 +210,13 @@ class fpdb: (nb, text) = data page = -1 #print "\n remove_tab: start", text - for i, tab in enumerate(self.nb_tabs): + for i, tab in enumerate(self.nb_tab_names): if text == tab: page = i #print " page =", page if page >= 0 and page < self.nb.get_n_pages(): #print " removing page", page - del self.nb_tabs[page] + del self.nb_tab_names[page] nb.remove_page(page) # Need to refresh the widget -- # This forces the widget to redraw itself. @@ -220,8 +240,39 @@ class fpdb: dia.set_authors(['Steffen', 'Eratosthenes', 's0rrow', 'EricBlade', '_mt', 'sqlcoder', 'Bostik', 'and others']) dia.set_program_name("Free Poker Database (FPDB)") + + db_version = "" + #if self.db is not None: + # db_version = self.db.get_version() + nums = [ ('Operating System', os.name) + , ('Python', sys.version[0:3]) + , ('GTK+', '.'.join([str(x) for x in gtk.gtk_version])) + , ('PyGTK', '.'.join([str(x) for x in gtk.pygtk_version])) + , ('matplotlib', matplotlib_version) + , ('numpy', numpy_version) + , ('sqlite3', sqlite3_version) + , ('sqlite', sqlite_version) + , ('database', self.settings['db-server'] + db_version) + ] + versions = gtk.TextBuffer() + w = 20 # width used for module names and version numbers + versions.set_text( '\n'.join( [x[0].rjust(w)+' '+ x[1].ljust(w) for x in nums] ) ) + view = gtk.TextView(versions) + view.set_editable(False) + view.set_justification(gtk.JUSTIFY_CENTER) + view.modify_font(pango.FontDescription('monospace 10')) + view.show() + dia.vbox.pack_end(view, True, True, 2) + l = gtk.Label('Version Information:') + l.set_alignment(0.5, 0.5) + l.show() + dia.vbox.pack_end(l, True, True, 2) + dia.run() dia.destroy() + log.debug("Threads: ") + for t in self.threads: + log.debug("........." + str(t.__class__)) def dia_preferences(self, widget, data=None): dia = gtk.Dialog("Preferences", @@ -229,12 +280,20 @@ class fpdb: gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)) - dia.set_default_size(500, 500) + dia.set_default_size(700, 500) + prefs = GuiPrefs.GuiPrefs(self.config, self.window, dia.vbox) response = dia.run() if response == gtk.RESPONSE_ACCEPT: # save updated config self.config.save() + if len(self.nb_tab_names) == 1: + # only main tab open, reload profile + self.load_profile() + else: + self.warning_box("Updated preferences have not been loaded because " + + "windows are open. Re-start fpdb to load them.") + dia.destroy() def dia_create_del_database(self, widget, data=None): @@ -430,28 +489,52 @@ class fpdb: self.release_global_lock() def dia_logs(self, widget, data=None): - lock_set = False - if self.obtain_global_lock(): - lock_set = True + """opens the log viewer window""" - dia = gtk.Dialog(title="Log Messages" - ,parent=None - ,flags=0 - ,buttons=(gtk.STOCK_CLOSE,gtk.RESPONSE_OK)) - logviewer = GuiLogView.GuiLogView(self.config, self.window, dia.vbox) - response = dia.run() - if response == gtk.RESPONSE_ACCEPT: - pass - dia.destroy() + #lock_set = False + #if self.obtain_global_lock(): + # lock_set = True - if lock_set: - self.release_global_lock() + # remove members from self.threads if close messages received + self.process_close_messages() + + viewer = None + for i, t in enumerate(self.threads): + if str(t.__class__) == 'GuiLogView.GuiLogView': + viewer = t + break + + if viewer is None: + #print "creating new log viewer" + new_thread = GuiLogView.GuiLogView(self.config, self.window, self.closeq) + self.threads.append(new_thread) + else: + #print "showing existing log viewer" + viewer.get_dialog().present() + + #if lock_set: + # self.release_global_lock() def addLogText(self, text): end_iter = self.logbuffer.get_end_iter() self.logbuffer.insert(end_iter, text) self.logview.scroll_to_mark(self.logbuffer.get_insert(), 0) + + def process_close_messages(self): + # check for close messages + try: + while True: + name = self.closeq.get(False) + for i, t in enumerate(self.threads): + if str(t.__class__) == str(name): + # thread has ended so remove from list: + del self.threads[i] + break + except Queue.Empty: + # no close messages on queue, do nothing + pass + def __calendar_dialog(self, widget, entry): self.dia_confirm.set_modal(False) d = gtk.Window(gtk.WINDOW_TOPLEVEL) @@ -572,7 +655,7 @@ class fpdb: ('LoadProf', None, '_Load Profile (broken)', 'L', 'Load your profile', self.dia_load_profile), ('EditProf', None, '_Edit Profile (todo)', 'E', 'Edit your profile', self.dia_edit_profile), ('SaveProf', None, '_Save Profile (todo)', 'S', 'Save your profile', self.dia_save_profile), - ('Preferences', None, '_Preferences', None, 'Edit your preferences', self.dia_preferences), + ('Preferences', None, 'Pre_ferences', 'F', 'Edit your preferences', self.dia_preferences), ('import', None, '_Import'), ('sethharchive', None, '_Set HandHistory Archive Directory', None, 'Set HandHistory Archive Directory', self.select_hhArchiveBase), ('bulkimp', None, '_Bulk Import', 'B', 'Bulk Import', self.tab_bulk_import), @@ -626,19 +709,26 @@ class fpdb: self.settings.update(self.config.get_import_parameters()) self.settings.update(self.config.get_default_paths()) - if self.db is not None and self.db.fdb is not None: + if self.db is not None and self.db.connected: self.db.disconnect() self.sql = SQL.Sql(db_server = self.settings['db-server']) + err_msg = None try: self.db = Database.Database(self.config, sql = self.sql) except Exceptions.FpdbMySQLAccessDenied: - self.warning_box("MySQL Server reports: Access denied. Are your permissions set correctly?") - exit() + err_msg = "MySQL Server reports: Access denied. Are your permissions set correctly?" except Exceptions.FpdbMySQLNoDatabase: - msg = "MySQL client reports: 2002 or 2003 error. Unable to connect - Please check that the MySQL service has been started" - self.warning_box(msg) - exit + err_msg = "MySQL client reports: 2002 or 2003 error. Unable to connect - " \ + + "Please check that the MySQL service has been started" + except Exceptions.FpdbPostgresqlAccessDenied: + err_msg = "Postgres Server reports: Access denied. Are your permissions set correctly?" + except Exceptions.FpdbPostgresqlNoDatabase: + err_msg = "Postgres client reports: Unable to connect - " \ + + "Please check that the Postgres service has been started" + if err_msg is not None: + self.db = None + self.warning_box(err_msg) # except FpdbMySQLFailedError: # self.warning_box("Unable to connect to MySQL! Is the MySQL server running?!", "FPDB ERROR") @@ -656,7 +746,7 @@ class fpdb: # print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]) # sys.stderr.write("Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user'])) - if self.db.wrongDbVersion: + if self.db is not None and self.db.wrongDbVersion: diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) label = gtk.Label("An invalid DB version or missing tables have been detected.") @@ -675,14 +765,15 @@ class fpdb: diaDbVersionWarning.destroy() if self.status_bar is None: - self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.database, self.db.host)) + self.status_bar = gtk.Label("") self.main_vbox.pack_end(self.status_bar, False, True, 0) self.status_bar.show() - else: - self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.database, self.db.host)) - # Database connected to successfully, load queries to pass on to other classes - self.db.rollback() + if self.db is not None and self.db.connected: + self.status_bar.set_text("Status: Connected to %s database named %s on host %s" + % (self.db.get_backend_name(),self.db.database, self.db.host)) + # rollback to make sure any locks are cleared: + self.db.rollback() self.validate_config() @@ -703,7 +794,8 @@ class fpdb: # TODO: can we get some / all of the stuff done in this function to execute on any kind of abort? print "Quitting normally" # TODO: check if current settings differ from profile, if so offer to save or abort - self.db.disconnect() + if self.db is not None and self.db.connected: + self.db.disconnect() self.statusIcon.set_visible(False) gtk.main_quit() @@ -770,7 +862,6 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") self.add_and_display_tab(gv_tab, "Graphs") def __init__(self): - self.threads = [] # no more than 1 process can this lock at a time: self.lock = interlocks.InterProcessLock(name="fpdb_global_lock") self.db = None @@ -794,14 +885,17 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") menubar.show() #done menubar + self.threads = [] # objects used by tabs - no need for threads, gtk handles it + self.closeq = Queue.Queue(20) # used to signal ending of a thread (only logviewer for now) + self.nb = gtk.Notebook() self.nb.set_show_tabs(True) self.nb.show() self.main_vbox.pack_start(self.nb, True, True, 0) - self.pages=[] - self.tabs=[] - self.tab_names=[] - self.nb_tabs=[] + self.tabs=[] # the event_boxes forming the actual tabs + self.tab_names=[] # names of tabs used since program started, not removed if tab is closed + self.pages=[] # the contents of the page, not removed if tab is closed + self.nb_tab_names=[] # list of tab names currently displayed in notebook self.tab_main_help(None, None) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index 405a142c..2d7f2e0c 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -129,18 +129,22 @@ class fpdb_db: self.db = psycopg2.connect(database = database) connected = True except: + # direct connection failed so try user/pass/... version pass - #msg = "PostgreSQL direct connection to database (%s) failed, trying with user ..." % (database,) - #print msg - #raise FpdbError(msg) if not connected: try: self.db = psycopg2.connect(host = host, user = user, password = password, database = database) - except: - msg = "PostgreSQL connection to database (%s) user (%s) failed. Are you sure the DB is running?" % (database, user) + except Exception, ex: + if 'Connection refused' in ex.args[0]: + # meaning eg. db not running + raise FpdbPostgresqlNoDatabase(errmsg = ex.args[0]) + elif 'password authentication' in ex.args[0]: + raise FpdbPostgresqlAccessDenied(errmsg = ex.args[0]) + else: + msg = ex.args[0] print msg raise FpdbError(msg) elif backend == fpdb_db.SQLITE: @@ -154,7 +158,7 @@ class fpdb_db: if not os.path.isdir(Configuration.DIR_DATABASES) and not database == ":memory:": print "Creating directory: '%s'" % (Configuration.DIR_DATABASES) os.mkdir(Configuration.DIR_DATABASES) - database = os.path.join(Configuration.DIR_DATABASE, database) + database = os.path.join(Configuration.DIR_DATABASES, database) 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") @@ -167,6 +171,7 @@ class fpdb_db: logging.warning("Some database functions will not work without NumPy support") else: raise FpdbError("unrecognised database backend:"+backend) + self.cursor = self.db.cursor() # Set up query dictionary as early in the connection process as we can. self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name()) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 760e1f01..dd46d7c6 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -435,12 +435,12 @@ class Importer: for hand in handlist: #try, except duplicates here? - #hand.prepInsert() + hand.prepInsert(self.database) hand.insert(self.database) if self.callHud and hand.dbid_hands != 0: #print "DEBUG: call to HUD: handsId: %s" % hand.dbid_hands #pipe the Hands.id out to the HUD - # print "fpdb_import: sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud + print "fpdb_import: sending hand to hud", hand.dbid_hands, "pipe =", self.caller.pipe_to_hud self.caller.pipe_to_hud.stdin.write("%s" % (hand.dbid_hands) + os.linesep) errors = getattr(hhc, 'numErrors') diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index aa9421f6..1c22f6e6 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -1247,13 +1247,13 @@ sure to also change the following storage method and table_viewer.prepare_data i if not isAllIn: isAllIn = any(i for i in allIns[1][player]) - elif len(action_types[2][player]) > 0: + if isAllIn or len(action_types[2][player]) > 0: if all(actiontype != "fold" for actiontype in action_types[1][player]): myStreet2Seen = True if not isAllIn: isAllAin = any(i for i in allIns[2][player]) - elif len(action_types[3][player]) > 0: + if isAllIn or len(action_types[3][player]) > 0: if all(actiontype != "fold" for actiontype in action_types[2][player]): myStreet3Seen = True @@ -1264,7 +1264,7 @@ sure to also change the following storage method and table_viewer.prepare_data i #print "in else" if not isAllIn: isAllIn = any(i for i in allIns[3][player]) - elif len(action_types[4][player]) > 0: + if isAllIn or len(action_types[4][player]) > 0: #print "in if" myStreet4Seen = True