Merge branch 'master' of git://git.assembla.com/fpdb-sql

This commit is contained in:
Worros 2009-12-07 12:09:05 +08:00
commit 49576b5eb3
9 changed files with 392 additions and 23 deletions

View File

@ -58,7 +58,8 @@ class Database:
PGSQL = 3
SQLITE = 4
hero_hudstart_def = '1999-12-31' # default for length of Hero's stats in HUD
hero_hudstart_def = '1999-12-31' # default for length of Hero's stats in HUD
villain_hudstart_def = '1999-12-31' # default for length of Villain's stats in HUD
# Data Structures for index and foreign key creation
# drop_code is an int with possible values: 0 - don't drop for bulk import
@ -473,8 +474,9 @@ class Database:
else:
h_seats_min, h_seats_max = 0, 10
print "bad h_seats_style value:", h_seats_style
print "opp seats style", seats_style, "hero seats style", h_seats_style
print "opp seats:", seats_min, seats_max, " hero seats:", h_seats_min, h_seats_max
log.info("opp seats style %s %d %d hero seats style %s %d %d"
% (seats_style, seats_min, seats_max
,h_seats_style, h_seats_min, h_seats_max) )
if hud_style == 'S' or h_hud_style == 'S':
self.get_stats_from_hand_session(hand, stat_dict, hero_id
@ -1324,7 +1326,7 @@ class Database:
self.dropAllForeignKeys()
self.createAllForeignKeys()
def rebuild_hudcache(self, start=None):
def rebuild_hudcache(self, h_start=None, v_start=None):
"""clears hudcache and rebuilds from the individual handsplayers records"""
try:
@ -1344,13 +1346,17 @@ class Database:
if p_id:
self.hero_ids[site_id] = int(p_id)
if start is None:
start = self.hero_hudstart_def
if h_start is None:
h_start = self.hero_hudstart_def
if v_start is None:
v_start = self.villain_hudstart_def
if self.hero_ids == {}:
where = ""
else:
where = "where hp.playerId not in " + str(tuple(self.hero_ids.values())) \
+ " or h.handStart > '" + start + "'"
where = "where ( hp.playerId not in " + str(tuple(self.hero_ids.values())) \
+ " and h.handStart > '" + v_start + "')" \
+ " or ( hp.playerId in " + str(tuple(self.hero_ids.values())) \
+ " and h.handStart > '" + h_start + "')"
rebuild_sql = self.sql.query['rebuildHudCache'].replace('<where_clause>', where)
self.get_cursor().execute(self.sql.query['clearHudCache'])

View File

@ -196,7 +196,10 @@ class GuiGraphViewer (threading.Thread):
self.ax.plot(green, color='green', label='Hands: %d\nProfit: $%.2f' %(len(green), green[-1]))
self.ax.plot(blue, color='blue', label='Showdown: $%.2f' %(blue[-1]))
self.ax.plot(red, color='red', label='Non-showdown: $%.2f' %(red[-1]))
self.ax.legend(loc='best', fancybox=True, shadow=True, prop=FontProperties(size='smaller'))
if sys.version[0:3] == '2.5':
self.ax.legend(loc='best', shadow=True, prop=FontProperties(size='smaller'))
else:
self.ax.legend(loc='best', fancybox=True, shadow=True, prop=FontProperties(size='smaller'))
self.graphBox.add(self.canvas)

175
pyfpdb/GuiLogView.py Executable file
View File

@ -0,0 +1,175 @@
#!/usr/bin/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 Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
import mmap
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import pango
import Configuration
log = Configuration.get_logger("logging.conf", "logview")
MAX_LINES = 100000
class GuiLogView:
def __init__(self, config, mainwin, vbox):
self.config = config
self.main_window = mainwin
self.vbox = 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
# filter = self.liststore.filter_new()
# filter.set_visible_column(1)
#
# # 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 = []
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.add(self.listview)
self.vbox.pack_start(scrolledwindow, expand=True, fill=True, padding=0)
refreshbutton = gtk.Button("Refresh")
refreshbutton.connect("clicked", self.refresh, None)
self.vbox.pack_start(refreshbutton, False, False, 3)
refreshbutton.show()
self.listview.show()
scrolledwindow.show()
self.vbox.show()
col = self.addColumn("Date/Time", 0)
col = self.addColumn("Module", 1)
col = self.addColumn("Level", 2)
col = self.addColumn("Text", 3)
self.loadLog()
self.vbox.show_all()
def addColumn(self, title, n):
col = gtk.TreeViewColumn(title)
self.listview.append_column(col)
cRender = gtk.CellRendererText()
cRender.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
col.pack_start(cRender, True)
col.add_attribute(cRender, 'text', n)
col.set_max_width(1000)
col.set_spacing(0) # no effect
self.listcols.append(col)
col.set_clickable(True)
col.connect("clicked", self.sortCols, n)
return(col)
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()
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()
def sortCols(self, col, n):
try:
if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING:
col.set_sort_order(gtk.SORT_DESCENDING)
else:
col.set_sort_order(gtk.SORT_ASCENDING)
self.liststore.set_sort_column_id(n, col.get_sort_order())
#self.liststore.set_sort_func(n, self.sortnums, (n,grid))
for i in xrange(len(self.listcols)):
self.listcols[i].set_sort_indicator(False)
self.listcols[n].set_sort_indicator(True)
# use this listcols[col].set_sort_indicator(True)
# to turn indicator off for other cols
except:
err = traceback.extract_tb(sys.exc_info()[2])
print "***sortCols error: " + str(sys.exc_info()[1])
print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] )
def refresh(self, widget, data):
self.loadLog()
if __name__=="__main__":
config = Configuration.Config()
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.set_title("Test Log Viewer")
win.set_border_width(1)
win.set_default_size(600, 500)
win.set_resizable(True)
dia = gtk.Dialog("Log Viewer",
win,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
dia.set_default_size(500, 500)
log = GuiLogView(config, win, dia.vbox)
response = dia.run()
if response == gtk.RESPONSE_ACCEPT:
pass
dia.destroy()

View File

@ -63,6 +63,9 @@ elif os.name == 'nt':
import Hud
log = Configuration.get_logger("logging.conf")
class HUD_main(object):
"""A main() object to own both the read_stdin thread and the gui."""
# This class mainly provides state for controlling the multiple HUDs.
@ -192,6 +195,8 @@ class HUD_main(object):
while 1: # wait for a new hand number on stdin
new_hand_id = sys.stdin.readline()
t0 = time.time()
t1 = t2 = t3 = t4 = t5 = t6 = t0
new_hand_id = string.rstrip(new_hand_id)
if new_hand_id == "": # blank line means quit
self.destroy()
@ -206,6 +211,7 @@ class HUD_main(object):
print "db error: skipping %s" % new_hand_id
sys.stderr.write("Database error: could not find hand %s.\n" % new_hand_id)
continue
t1 = time.time()
if type == "tour": # hand is from a tournament
temp_key = tour_number
@ -215,6 +221,12 @@ class HUD_main(object):
# Update an existing HUD
if temp_key in self.hud_dict:
# get stats using hud's specific params and get cards
self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days']
, self.hud_dict[temp_key].hud_params['h_hud_days'])
t2 = time.time()
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params
,self.hero_ids[site_id], num_seats)
t3 = time.time()
try:
self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days']
, self.hud_dict[temp_key].hud_params['h_hud_days'])
@ -238,8 +250,10 @@ class HUD_main(object):
else:
# get stats using default params--also get cards
self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] )
t4 = time.time()
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params
,self.hero_ids[site_id], num_seats)
t5 = time.time()
cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud!
@ -263,6 +277,9 @@ class HUD_main(object):
else:
sys.stderr.write('Table "%s" no longer exists\n' % table_name)
t6 = time.time()
log.info("HUD_main.read_stdin: hand read in %4.3f seconds (%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f)"
% (t6-t0,t1-t0,t2-t0,t3-t0,t4-t0,t5-t0,t6-t0))
self.db_connection.connection.rollback()
if __name__== "__main__":

View File

@ -1317,6 +1317,7 @@ class Sql:
1.25 would be a config value so user could change it)
*/
GROUP BY hc.PlayerId, hp.seatNo, p.name
ORDER BY hc.PlayerId, hp.seatNo, p.name
"""
# same as above except stats are aggregated for all blind/limit levels
@ -1418,6 +1419,7 @@ class Sql:
)
)
GROUP BY hc.PlayerId, p.name
ORDER BY hc.PlayerId, p.name
"""
# NOTES on above cursor:
# - Do NOT include %s inside query in a comment - the db api thinks

View File

@ -60,7 +60,8 @@ if not options.errorsToConsole:
errorFile = open('fpdb-error-log.txt', 'w', 0)
sys.stderr = errorFile
import logging
#import logging
import logging, logging.config
import pygtk
pygtk.require('2.0')
@ -70,6 +71,7 @@ import interlocks
import GuiPrefs
import GuiLogView
import GuiBulkImport
import GuiPlayerStats
import GuiPositionalStats
@ -85,6 +87,8 @@ import Exceptions
VERSION = "0.12"
log = Configuration.get_logger("logging.conf", "fpdb")
class fpdb:
def tab_clicked(self, widget, tab_name):
"""called when a tab button is clicked to activate that tab"""
@ -334,27 +338,48 @@ class fpdb:
diastring = "Please confirm that you want to re-create the HUD cache."
self.dia_confirm.format_secondary_text(diastring)
hb = gtk.HBox(True, 1)
hb1 = gtk.HBox(True, 1)
self.h_start_date = gtk.Entry(max=12)
self.h_start_date.set_text( self.db.get_hero_hudcache_start() )
lbl = gtk.Label(" Hero's cache starts: ")
btn = gtk.Button()
btn.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn.connect('clicked', self.__calendar_dialog, self.h_start_date)
hb1.pack_start(lbl, expand=True, padding=3)
hb1.pack_start(self.h_start_date, expand=True, padding=2)
hb1.pack_start(btn, expand=False, padding=3)
self.dia_confirm.vbox.add(hb1)
hb1.show_all()
hb2 = gtk.HBox(True, 1)
self.start_date = gtk.Entry(max=12)
self.start_date.set_text( self.db.get_hero_hudcache_start() )
lbl = gtk.Label(" Hero's cache starts: ")
lbl = gtk.Label(" Villains' cache starts: ")
btn = gtk.Button()
btn.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn.connect('clicked', self.__calendar_dialog, self.start_date)
hb.pack_start(lbl, expand=True, padding=3)
hb.pack_start(self.start_date, expand=True, padding=2)
hb.pack_start(btn, expand=False, padding=3)
self.dia_confirm.vbox.add(hb)
hb.show_all()
hb2.pack_start(lbl, expand=True, padding=3)
hb2.pack_start(self.start_date, expand=True, padding=2)
hb2.pack_start(btn, expand=False, padding=3)
self.dia_confirm.vbox.add(hb2)
hb2.show_all()
response = self.dia_confirm.run()
self.dia_confirm.destroy()
if response == gtk.RESPONSE_YES:
self.db.rebuild_hudcache( self.start_date.get_text() )
lbl = gtk.Label(" Rebuilding HUD Cache ... ")
self.dia_confirm.vbox.add(lbl)
lbl.show()
while gtk.events_pending():
gtk.main_iteration_do(False)
self.db.rebuild_hudcache( self.h_start_date.get_text(), self.start_date.get_text() )
elif response == gtk.RESPONSE_NO:
print 'User cancelled rebuilding hud cache'
self.dia_confirm.destroy()
self.release_global_lock()
def dia_rebuild_indexes(self, widget, data=None):
@ -368,16 +393,53 @@ class fpdb:
self.dia_confirm.format_secondary_text(diastring)
response = self.dia_confirm.run()
self.dia_confirm.destroy()
if response == gtk.RESPONSE_YES:
lbl = gtk.Label(" Rebuilding Indexes ... ")
self.dia_confirm.vbox.add(lbl)
lbl.show()
while gtk.events_pending():
gtk.main_iteration_do(False)
self.db.rebuild_indexes()
lbl.set_text(" Cleaning Database ... ")
while gtk.events_pending():
gtk.main_iteration_do(False)
self.db.vacuumDB()
lbl.set_text(" Analyzing Database ... ")
while gtk.events_pending():
gtk.main_iteration_do(False)
self.db.analyzeDB()
elif response == gtk.RESPONSE_NO:
print 'User cancelled rebuilding db indexes'
self.dia_confirm.destroy()
self.release_global_lock()
def dia_logs(self, widget, data=None):
lock_set = False
if self.obtain_global_lock():
lock_set = True
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()
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 __calendar_dialog(self, widget, entry):
self.dia_confirm.set_modal(False)
d = gtk.Window(gtk.WINDOW_TOPLEVEL)
@ -397,10 +459,13 @@ class fpdb:
d.show_all()
def __get_dates(self):
t1 = self.start_date.get_text()
t1 = self.h_start_date.get_text()
if t1 == '':
t1 = '1970-01-01'
return (t1)
t2 = self.start_date.get_text()
if t2 == '':
t2 = '1970-01-01'
return (t1, t2)
def __get_date(self, widget, calendar, entry, win):
# year and day are correct, month is 0..11
@ -477,6 +542,7 @@ class fpdb:
</menu>
<menu action="help">
<menuitem action="Abbrev"/>
<menuitem action="Logs"/>
<separator/>
<menuitem action="About"/>
<menuitem action="License"/>
@ -518,6 +584,7 @@ class fpdb:
('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats),
('help', None, '_Help'),
('Abbrev', None, '_Abbrevations (todo)', None, 'List of Abbrevations', self.tab_abbreviations),
('Logs', None, '_Log Messages', None, 'Log and Debug Messages', self.dia_logs),
('About', None, 'A_bout', None, 'About the program', self.dia_about),
('License', None, '_License and Copying (todo)', None, 'License and Copying', self.dia_licensing),
])
@ -835,6 +902,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
gtk.main()
return 0
if __name__ == "__main__":
me = fpdb()
me.main()

View File

@ -11,6 +11,18 @@ keys=fileFormatter,stderrFormatter
level=INFO
handlers=consoleHandler,fileHandler
[logger_fpdb]
level=INFO
handlers=consoleHandler,fileHandler
qualname=fpdb
propagate=0
[logger_logview]
level=INFO
handlers=consoleHandler,fileHandler
qualname=logview
propagate=0
[logger_parser]
level=INFO
handlers=consoleHandler,fileHandler
@ -24,7 +36,7 @@ qualname=importer
propagate=0
[logger_config]
level=DEBUG
level=INFO
handlers=consoleHandler,fileHandler
qualname=config
propagate=0

29
pyfpdb/test1.py Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
"""test1.py
Test if python is working.
"""
# Copyright 2008, Ray E. Barker
#
# 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
print "\npython is working!"
print "\npress return to finish"
sys.stdin.readline()

57
pyfpdb/test2.py Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env python
"""test2.py
Test if gtk is working.
"""
# Copyright 2008, Ray E. Barker
#
# 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
try:
import pygtk
pygtk.require('2.0')
import gtk
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.set_title("Test GTK")
win.set_border_width(1)
win.set_default_size(600, 500)
win.set_resizable(True)
#win.show()
dia = gtk.Dialog("Test GTK",
win,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
dia.set_default_size(500, 300)
l = gtk.Label("GTK is working!")
dia.vbox.add(l)
l.show()
response = dia.run()
if response == gtk.RESPONSE_ACCEPT:
pass
dia.destroy()
except:
print "\nError:", sys.exc_info()
print "\npress return to finish"
sys.stdin.readline()