From anadelonbrin at users.sourceforge.net Wed Dec 1 00:00:13 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 1 00:00:18 2004
Subject: [Spambayes-checkins] spambayes setup.py,1.30,1.31
Message-ID:
Update of /cvsroot/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29241
Modified Files:
setup.py
Log Message:
Update PyPI details and an error message.
For convenience, when doing an sdist, get the script to print out an MD5 checksum
and the size of the created archive(s) for us. If only I could figure a way to get
Inno to do this, too ;)
Index: setup.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/setup.py,v
retrieving revision 1.30
retrieving revision 1.31
diff -C2 -d -r1.30 -r1.31
*** setup.py 13 Apr 2004 13:24:11 -0000 1.30
--- setup.py 30 Nov 2004 23:00:11 -0000 1.31
***************
*** 15,19 ****
if email.__version__ < '2.4.3':
print "Error: email package version < 2.4.3 found - need newer version"
! print "See INTEGRATION.txt for download information for email package"
sys.exit(0)
--- 15,19 ----
if email.__version__ < '2.4.3':
print "Error: email package version < 2.4.3 found - need newer version"
! print "See README.txt for download information for email package"
sys.exit(0)
***************
*** 74,77 ****
--- 74,91 ----
return parent.run(self)
+ import distutils.command.sdist
+ parent = distutils.command.sdist.sdist
+ class sdist(parent):
+ """Like the standard sdist, but also prints out MD5 checksums and sizes
+ for the created files, for convenience."""
+ def run(self):
+ import md5
+ retval = parent.run(self)
+ for archive in self.get_archive_files():
+ data = file(archive, "rb").read()
+ print '\n', archive, "\n\tMD5:", md5.md5(data).hexdigest()
+ print "\tLength:", len(data)
+ return retval
+
scripts=['scripts/sb_client.py',
'scripts/sb_dbexpimp.py',
***************
*** 107,111 ****
author_email = "spambayes@python.org",
url = "http://spambayes.sourceforge.net",
! cmdclass = {'install_scripts': install_scripts},
scripts=scripts,
packages = [
--- 121,127 ----
author_email = "spambayes@python.org",
url = "http://spambayes.sourceforge.net",
! cmdclass = {'install_scripts': install_scripts,
! 'sdist': sdist,
! },
scripts=scripts,
packages = [
***************
*** 114,119 ****
],
classifiers = [
! 'Development Status :: 4 - Beta',
'Environment :: Console',
'License :: OSI Approved :: Python Software Foundation License',
'Operating System :: POSIX',
--- 130,137 ----
],
classifiers = [
! 'Development Status :: 5 - Production/Stable',
'Environment :: Console',
+ 'Environment :: Plugins',
+ 'Environment :: Win32 (MS Windows)',
'License :: OSI Approved :: Python Software Foundation License',
'Operating System :: POSIX',
***************
*** 121,128 ****
--- 139,149 ----
'Operating System :: Microsoft :: Windows :: Windows 95/98/2000',
'Operating System :: Microsoft :: Windows :: Windows NT/2000',
+ 'Natural Language :: English',
'Programming Language :: Python',
+ 'Programming Language :: C',
'Intended Audience :: End Users/Desktop',
'Topic :: Communications :: Email :: Filters',
'Topic :: Communications :: Email :: Post-Office :: POP3',
+ 'Topic :: Communications :: Email :: Post-Office :: IMAP',
],
)
From anadelonbrin at users.sourceforge.net Wed Dec 1 00:49:40 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 1 00:49:44 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_imapfilter.py,1.44,1.45
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7418/scripts
Modified Files:
sb_imapfilter.py
Log Message:
There's a tricky situation where if use_ssl is False, but we
try to connect to a IMAP over SSL server, we will just hang
forever, waiting for a repsonse that will never come. To
get past this, just for the welcome message, we install a
timeout on the connection. Normal service is then returned.
This only applies when we are not using SSL.
Index: sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -C2 -d -r1.44 -r1.45
*** sb_imapfilter.py 22 Nov 2004 00:02:28 -0000 1.44
--- sb_imapfilter.py 30 Nov 2004 23:49:23 -0000 1.45
***************
*** 127,138 ****
--- 127,153 ----
'''A class extending the IMAP4 class, with a few optimizations'''
+ timeout = 60 # seconds
def __init__(self, server, port, debug=0, do_expunge=False):
+ # There's a tricky situation where if use_ssl is False, but we
+ # try to connect to a IMAP over SSL server, we will just hang
+ # forever, waiting for a repsonse that will never come. To
+ # get past this, just for the welcome message, we install a
+ # timeout on the connection. Normal service is then returned.
+ # This only applies when we are not using SSL.
+ if not hasattr(self, "ssl"):
+ readline = self.readline
+ self.readline = self.readline_timeout
try:
BaseIMAP.__init__(self, server, port)
except (BaseIMAP.error, socket.gaierror, socket.error):
print "Cannot connect to server %s on port %s" % (server, port)
+ if not hasattr(self, "ssl"):
+ print "If you are connecting to an SSL server, please " \
+ "ensure that you have the 'Use SSL' option enabled."
self.connected = False
else:
self.connected = True
+ if not hasattr(self, "ssl"):
+ self.readline = readline
self.debug = debug
self.do_expunge = do_expunge
***************
*** 145,148 ****
--- 160,188 ----
self.current_folder = None
+ def readline_timeout(self):
+ """Read line from remote, possibly timing out."""
+ st_time = time.time()
+ self.sock.setblocking(False)
+ buffer = []
+ while True:
+ if (time.time() - st_time) > self.timeout:
+ if options["globals", "verbose"]:
+ print >> sys.stderr, "IMAP Timing out"
+ break
+ try:
+ data = self.sock.recv(1)
+ except socket.error, e:
+ if e[0] == 10035:
+ # Nothing to receive, keep going.
+ continue
+ raise
+ if not data:
+ break
+ if data == '\n':
+ break
+ buffer.append(data)
+ self.sock.setblocking(True)
+ return "".join(buffer)
+
def login(self, username, pwd):
"""Log in to the IMAP server, catching invalid username/password."""
From anadelonbrin at users.sourceforge.net Fri Dec 3 06:09:45 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Fri Dec 3 06:09:50 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_notesfilter.py,1.8,1.9
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11624/scripts
Modified Files:
sb_notesfilter.py
Log Message:
Add improvements from Hugo Duncan:
Add ability to log to a Notes log.
Add ability to specify the name of the index file.
Improve message headers.
Add ability to specify password
Save spam field
Connect to database remotely, if possible
Plus some misc cleanup.
Index: sb_notesfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_notesfilter.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** sb_notesfilter.py 3 May 2004 02:08:13 -0000 1.8
--- sb_notesfilter.py 3 Dec 2004 05:09:42 -0000 1.9
***************
*** 1,11 ****
#! /usr/bin/env python
! '''sb_notesfilter.py - Lotus Notes Spambayes interface.
!
! Classes:
!
! Abstract:
! This module uses Spambayes as a filter against a Lotus Notes mail
database. The Notes client must be running when this process is
executed.
--- 1,7 ----
#! /usr/bin/env python
! '''sb_notesfilter.py - Lotus Notes SpamBayes interface.
! This module uses SpamBayes as a filter against a Lotus Notes mail
database. The Notes client must be running when this process is
executed.
***************
*** 45,49 ****
folder for Ham that has mistakenly been classified as Spam. If
there is any there, move it to the Train as Ham folder, so
! Spambayes will be less likely to make this mistake again.
Mail that is classified as Ham or Unsure is left in the inbox.
--- 41,45 ----
folder for Ham that has mistakenly been classified as Spam. If
there is any there, move it to the Train as Ham folder, so
! SpamBayes will be less likely to make this mistake again.
Mail that is classified as Ham or Unsure is left in the inbox.
***************
*** 53,57 ****
You should occasionally select some Ham and move it to the Train
as Ham folder, so Spambayes can tell the difference between Spam
! and Ham. The goal is to maintain a relative balance between the
number of Spam and the number of Ham that have been trained into
the database. These numbers are reported every time this program
--- 49,53 ----
You should occasionally select some Ham and move it to the Train
as Ham folder, so Spambayes can tell the difference between Spam
! and Ham. The goal is to maintain an approximate balance between the
number of Spam and the number of Ham that have been trained into
the database. These numbers are reported every time this program
***************
*** 59,67 ****
the amount of Ham you receive, it may be very difficult to
maintain this balance. This is not a matter of great concern.
! Spambayes will still make very few mistakes in this circumstance.
But, if this is the case, you should review your Spam folder for
falsely classified Ham, and retrain those that you find, on a
regular basis. This will prevent statistical error accumulation,
! which if allowed to continue, would cause Spambayes to tend to
classify everything as Spam.
--- 55,63 ----
the amount of Ham you receive, it may be very difficult to
maintain this balance. This is not a matter of great concern.
! SpamBayes will still make very few mistakes in this circumstance.
But, if this is the case, you should review your Spam folder for
falsely classified Ham, and retrain those that you find, on a
regular basis. This will prevent statistical error accumulation,
! which if allowed to continue, would cause SpamBayes to tend to
classify everything as Spam.
***************
*** 70,74 ****
it keeps a pickled dictionary of notes mail ids, so that once a
mail has been classified, it will not be classified again. The
! non-existence of is index file, named .sbindex,
indicates to the system that this is an initialization execution.
Rather than classify the inbox in this case, the contents of the
--- 66,70 ----
it keeps a pickled dictionary of notes mail ids, so that once a
mail has been classified, it will not be classified again. The
! non-existence of this index file, named .sbindex,
indicates to the system that this is an initialization execution.
Rather than classify the inbox in this case, the contents of the
***************
*** 91,95 ****
e.g. d27ml602/27/M/IBM
if specified, will initiate a replication
! -f folder : Name of spambayes folder
must have subfolders: Spam
Ham
--- 87,91 ----
e.g. d27ml602/27/M/IBM
if specified, will initiate a replication
! -f folder : Name of SpamBayes folder
must have subfolders: Spam
Ham
***************
*** 103,106 ****
--- 99,105 ----
statistics output would otherwise be lost when the
window closes.
+ -i filename : index file name
+ -W : password
+ -L dbname : log to database (template alog4.ntf)
-o section:option:value :
set [section, option] in the options database
***************
*** 124,131 ****
o sb_server style training/configuration interface?
o parameter to retrain?
o Suggestions?
'''
! # This module is part of the spambayes project, which is Copyright 2002
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
--- 123,131 ----
o sb_server style training/configuration interface?
o parameter to retrain?
+ o Use spambayes.message MessageInfo db's rather than own database.
o Suggestions?
'''
! # This module is part of the spambayes project, which is Copyright 2002-5
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
***************
*** 154,158 ****
! def classifyInbox(v, vmoveto, bayes, ldbname, notesindex):
# the notesindex hash ensures that a message is looked at only once
--- 154,158 ----
! def classifyInbox(v, vmoveto, bayes, ldbname, notesindex, log):
# the notesindex hash ensures that a message is looked at only once
***************
*** 176,200 ****
else:
if not notesindex.has_key(nid):
-
numdocs += 1
# Notes returns strings in unicode, and the Python
! # uni-decoder has trouble with these strings when
# you try to print them. So don't...
! # The com interface returns basic data types as tuples
! # only, thus the subscript on GetItemValue
!
! try:
! subj = doc.GetItemValue('Subject')[0]
! except:
! subj = 'No Subject'
!
! try:
! body = doc.GetItemValue('Body')[0]
! except:
! body = 'No Body'
!
! message = "Subject: %s\r\n\r\n%s" % (subj, body)
# generate_long_skips = True blows up on occasion,
--- 176,186 ----
else:
if not notesindex.has_key(nid):
numdocs += 1
# Notes returns strings in unicode, and the Python
! # decoder has trouble with these strings when
# you try to print them. So don't...
! message = getMessage(doc)
# generate_long_skips = True blows up on occasion,
***************
*** 218,261 ****
try:
print "%s spamprob is %s" % (subj[:30], prob)
except UnicodeError:
print " spamprob is %s" % (prob)
doc = v.GetNextDocument(doc)
# docstomove list is built because moving documents in the middle of
! # the classification loop looses the iterator position
for doc in docstomove:
doc.RemoveFromFolder(v.Name)
doc.PutInFolder(vmoveto.Name)
! print "%s documents processed" % (numdocs)
! print " %s classified as spam" % (numspam)
! print " %s classified as ham" % (numham)
! print " %s classified as unsure" % (numuns)
! def processAndTrain(v, vmoveto, bayes, is_spam, notesindex):
if is_spam:
! str = options["Headers", "header_spam_string"]
else:
! str = options["Headers", "header_ham_string"]
! print "Training %s" % (str)
docstomove = []
doc = v.GetFirstDocument()
while doc:
! try:
! subj = doc.GetItemValue('Subject')[0]
! except:
! subj = 'No Subject'
!
! try:
! body = doc.GetItemValue('Body')[0]
! except:
! body = 'No Body'
!
! message = "Subject: %s\r\n%s" % (subj, body)
options["Tokenizer", "generate_long_skips"] = False
--- 204,273 ----
try:
print "%s spamprob is %s" % (subj[:30], prob)
+ if log:
+ log.LogAction("%s spamprob is %s" % (subj[:30],
+ prob))
except UnicodeError:
print " spamprob is %s" % (prob)
+ if log:
+ log.LogAction(" spamprob " \
+ "is %s" % (prob,))
+
+ item = doc.ReplaceItemValue("Spam", prob)
+ item.IsSummary = True
+ doc.save(False, True, False)
doc = v.GetNextDocument(doc)
# docstomove list is built because moving documents in the middle of
! # the classification loop loses the iterator position
for doc in docstomove:
doc.RemoveFromFolder(v.Name)
doc.PutInFolder(vmoveto.Name)
! print "%s documents processed" % (numdocs,)
! print " %s classified as spam" % (numspam,)
! print " %s classified as ham" % (numham,)
! print " %s classified as unsure" % (numuns,)
! if log:
! log.LogAction("%s documents processed" % (numdocs,))
! log.LogAction(" %s classified as spam" % (numspam,))
! log.LogAction(" %s classified as ham" % (numham,))
! log.LogAction(" %s classified as unsure" % (numuns,))
+ def getMessage(doc):
+ try:
+ subj = doc.GetItemValue('Subject')[0]
+ except:
+ subj = 'No Subject'
! try:
! body = doc.GetItemValue('Body')[0]
! except:
! body = 'No Body'
!
! hdrs = ''
! for item in doc.Items:
! if item.Name == "From" or item.Name == "Sender" or \
! item.Name == "Received" or item.Name == "ReplyTo":
! try:
! hdrs = hdrs + ( "%s: %s\r\n" % (item.Name, item.Text) )
! except:
! hdrs = ''
!
! message = "%sSubject: %s\r\n\r\n%s" % (hdrs, subj, body)
! return message
+ def processAndTrain(v, vmoveto, bayes, is_spam, notesindex, log):
if is_spam:
! header_str = options["Headers", "header_spam_string"]
else:
! header_str = options["Headers", "header_ham_string"]
! print "Training %s" % (header_str,)
docstomove = []
doc = v.GetFirstDocument()
while doc:
! message = getMessage(doc)
options["Tokenizer", "generate_long_skips"] = False
***************
*** 276,280 ****
bayes.learn(tokens, is_spam)
! notesindex[nid] = str
docstomove += [doc]
doc = v.GetNextDocument(doc)
--- 288,292 ----
bayes.learn(tokens, is_spam)
! notesindex[nid] = header_str
docstomove += [doc]
doc = v.GetNextDocument(doc)
***************
*** 284,300 ****
doc.PutInFolder(vmoveto.Name)
! print "%s documents trained" % (len(docstomove))
! def run(bdbname, useDBM, ldbname, rdbname, foldname, doTrain, doClassify):
bayes = storage.open_storage(bdbname, useDBM)
try:
! fp = open("%s.sbindex" % (ldbname), 'rb')
except IOError, e:
! if e.errno != errno.ENOENT: raise
notesindex = {}
! print "%s.sbindex file not found, this is a first time run" \
! % (ldbname)
print "No classification will be performed"
else:
--- 296,315 ----
doc.PutInFolder(vmoveto.Name)
! print "%s documents trained" % (len(docstomove),)
! if log:
! log.LogAction("%s documents trained" % (len(docstomove),))
! def run(bdbname, useDBM, ldbname, rdbname, foldname, doTrain, doClassify,
! pwd, idxname, logname):
bayes = storage.open_storage(bdbname, useDBM)
try:
! fp = open(idxname, 'rb')
except IOError, e:
! if e.errno != errno.ENOENT:
! raise
notesindex = {}
! print "%s file not found, this is a first time run" % (idxname,)
print "No classification will be performed"
else:
***************
*** 302,347 ****
fp.close()
sess = win32com.client.Dispatch("Lotus.NotesSession")
try:
! sess.initialize()
except pywintypes.com_error:
print "Session aborted"
sys.exit()
! db = sess.GetDatabase("",ldbname)
vinbox = db.getView('($Inbox)')
! vspam = db.getView("%s\Spam" % (foldname))
! vham = db.getView("%s\Ham" % (foldname))
! vtrainspam = db.getView("%s\Train as Spam" % (foldname))
! vtrainham = db.getView("%s\Train as Ham" % (foldname))
if doTrain:
! processAndTrain(vtrainspam, vspam, bayes, True, notesindex)
# for some reason, using inbox as a target here loses the mail
! processAndTrain(vtrainham, vham, bayes, False, notesindex)
! if rdbname:
! print "Replicating..."
! db.Replicate(rdbname)
! print "Done"
if doClassify:
! classifyInbox(vinbox, vtrainspam, bayes, ldbname, notesindex)
print "The Spambayes database currently has %s Spam and %s Ham" \
! % (bayes.nspam, bayes.nham)
bayes.store()
! fp = open("%s.sbindex" % (ldbname), 'wb')
pickle.dump(notesindex, fp)
fp.close()
- if __name__ == '__main__':
try:
! opts, args = getopt.getopt(sys.argv[1:], 'htcPd:p:l:r:f:o:')
except getopt.error, msg:
print >>sys.stderr, str(msg) + '\n\n' + __doc__
--- 317,393 ----
fp.close()
+ need_replicate = False
+
sess = win32com.client.Dispatch("Lotus.NotesSession")
try:
! if pwd:
! sess.initialize(pwd)
! else:
! sess.initialize()
except pywintypes.com_error:
print "Session aborted"
sys.exit()
+ try:
+ db = sess.GetDatabase(rdbname, ldbname)
+ except pywintypes.com_error:
+ if rdbname:
+ print "Could not open database remotely, trying locally"
+ try:
+ db = sess.GetDatabase("", ldbname)
+ need_replicate = True
+ except pywintypes.com_error:
+ print "Could not open database"
+ sys.exit()
+ else:
+ raise
! log = sess.CreateLog("SpambayesAgentLog")
! try:
! log.OpenNotesLog("", logname)
! except pywintypes.com_error:
! print "Could not open log"
! log = None
!
! if log:
! log.LogAction("Running spambayes")
vinbox = db.getView('($Inbox)')
! vspam = db.getView("%s\Spam" % (foldname,))
! vham = db.getView("%s\Ham" % (foldname,))
! vtrainspam = db.getView("%s\Train as Spam" % (foldname,))
! vtrainham = db.getView("%s\Train as Ham" % (foldname,))
if doTrain:
! processAndTrain(vtrainspam, vspam, bayes, True, notesindex, log)
# for some reason, using inbox as a target here loses the mail
! processAndTrain(vtrainham, vham, bayes, False, notesindex, log)
! if need_replicate:
! try:
! print "Replicating..."
! db.Replicate(rdbname)
! print "Done"
! except pywintypes.com_error:
! print "Could not replicate"
if doClassify:
! classifyInbox(vinbox, vtrainspam, bayes, ldbname, notesindex, log)
print "The Spambayes database currently has %s Spam and %s Ham" \
! % (bayes.nspam, bayes.nham)
bayes.store()
! fp = open(idxname), 'wb')
pickle.dump(notesindex, fp)
fp.close()
+ if log:
+ log.LogAction("Finished running spambayes")
+ if __name__ == '__main__':
try:
! opts, args = getopt.getopt(sys.argv[1:], 'htcPd:p:l:r:f:o:i:W:L:')
except getopt.error, msg:
print >>sys.stderr, str(msg) + '\n\n' + __doc__
***************
*** 351,354 ****
--- 397,403 ----
rdbname = None # remote notes database location
sbfname = None # spambayes folder name
+ idxname = None # index file name
+ logname = None # log database name
+ pwd = None # password
doTrain = False
doClassify = False
***************
*** 371,381 ****
elif opt == '-P':
doPrompt = True
elif opt == '-o':
options.set_from_cmdline(arg, sys.stderr)
bdbname, useDBM = storage.database_type(opts)
if (bdbname and ldbname and sbfname and (doTrain or doClassify)):
run(bdbname, useDBM, ldbname, rdbname, \
! sbfname, doTrain, doClassify)
if doPrompt:
--- 420,439 ----
elif opt == '-P':
doPrompt = True
+ elif opt == '-i':
+ idxname = arg
+ elif opt == '-L':
+ logname = arg
+ elif opt == '-W':
+ pwd = arg
elif opt == '-o':
options.set_from_cmdline(arg, sys.stderr)
bdbname, useDBM = storage.database_type(opts)
+ if not idxname:
+ idxname = "%s.sbindex" % (ldbname)
+
if (bdbname and ldbname and sbfname and (doTrain or doClassify)):
run(bdbname, useDBM, ldbname, rdbname, \
! sbfname, doTrain, doClassify, pwd, idxname, logname)
if doPrompt:
From kpitt at users.sourceforge.net Fri Dec 3 22:43:23 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Fri Dec 3 22:43:26 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py, 1.140,
1.141 config.py, 1.33, 1.34 manager.py, 1.99, 1.100
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4934/Outlook2000
Modified Files:
addin.py config.py manager.py
Log Message:
Add notification sound support as per patch #858925.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.140
retrieving revision 1.141
diff -C2 -d -r1.140 -r1.141
*** addin.py 26 Nov 2004 03:11:43 -0000 1.140
--- addin.py 3 Dec 2004 21:43:19 -0000 1.141
***************
*** 223,226 ****
--- 223,228 ----
folder_name,
disposition)
+
+ manager.HandleNotification(disposition)
else:
print "Spam filtering is disabled - ignoring new message"
Index: config.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/config.py,v
retrieving revision 1.33
retrieving revision 1.34
diff -C2 -d -r1.33 -r1.34
*** config.py 2 Nov 2004 21:37:37 -0000 1.33
--- config.py 3 Dec 2004 21:43:19 -0000 1.34
***************
*** 28,32 ****
from spambayes.OptionsClass import OptionsClass, Option
from spambayes.OptionsClass import RESTORE, DO_NOT_RESTORE
! from spambayes.OptionsClass import BOOLEAN, INTEGER, REAL, PATH
class FolderIDOption(Option):
--- 28,32 ----
from spambayes.OptionsClass import OptionsClass, Option
from spambayes.OptionsClass import RESTORE, DO_NOT_RESTORE
! from spambayes.OptionsClass import BOOLEAN, INTEGER, REAL, PATH, FILE_WITH_PATH
class FolderIDOption(Option):
***************
*** 269,272 ****
--- 269,309 ----
BOOLEAN, RESTORE),
),
+
+ # These options control how the user is notified of new messages.
+ "Notification": (
+ ("notify_sound_enabled", _("Play a notification sound when new messages arrive?"), False,
+ _("""If enabled, SpamBayes will play a notification sound after a
+ batch of new messages is processed. A different sound can be
+ assigned to each of the three classifications of messages. The
+ good sound will be played if any good messages are received. The
+ possible spam sound will be played if unsure messages are received,
+ but no good messages. The spam sound will be played if all
+ received messages are spam."""),
+ BOOLEAN, RESTORE),
+ ("notify_ham_sound", _("Sound file to play for good messages"), "",
+ _("""Specifies the full path to a Windows sound file (WAV format) that
+ will be played as notification that a good message has been received."""),
+ FILE_WITH_PATH, DO_NOT_RESTORE),
+ ("notify_unsure_sound", _("Sound file to play for possible spam messages"), "",
+ _("""Specifies the full path to a Windows sound file (WAV format) that
+ will be played as notification that a possible spam message has been
+ received. The possible spam notification sound will only be played
+ if no good messages have been received."""),
+ FILE_WITH_PATH, DO_NOT_RESTORE),
+ ("notify_spam_sound", _("Sound file to play for spam messages"), "",
+ _("""Specifies the full path to a Windows sound file (WAV format) that
+ will be played as notification that a spam message has been
+ received. The spam notification sound will only be played if no
+ good or possible spam messages have been received."""),
+ FILE_WITH_PATH, DO_NOT_RESTORE),
+ ("notify_accumulate_delay", _("The delay time to wait for additional received messages (in seconds)"), 10.0,
+ _("""When SpamBayes classifies a new message, it sets a timer to wait
+ for additional new messages. If another new message is received
+ before the timer expires then the delay time is reset and SpamBayes
+ continues to wait. If no new messages arrive within the delay time
+ the SpamBayes will play the appropriate notification sound for the
+ received messages."""),
+ REAL, RESTORE),
+ ),
}
Index: manager.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
retrieving revision 1.99
retrieving revision 1.100
diff -C2 -d -r1.99 -r1.100
*** manager.py 25 Nov 2004 23:26:58 -0000 1.99
--- manager.py 3 Dec 2004 21:43:19 -0000 1.100
***************
*** 10,13 ****
--- 10,15 ----
import win32api, win32con, win32gui
+ import timer, thread
+
import win32com.client
import win32com.client.gencache
***************
*** 331,334 ****
--- 333,337 ----
class BayesManager:
def __init__(self, config_base="default", outlook=None, verbose=0):
+ self.owner_thread_ident = thread.get_ident() # check we aren't multi-threaded
self.never_configured = True
self.reported_error_map = {}
***************
*** 340,343 ****
--- 343,348 ----
self.dialog_parser = None
self.test_suite_running = False
+ self.received_ham = self.received_unsure = self.received_spam = 0
+ self.notify_timer_id = None
import_early_core_spambayes_stuff()
***************
*** 786,789 ****
--- 791,795 ----
def Close(self):
global _mgr
+ self._KillNotifyTimer()
self.classifier_data.Close()
self.config = self.options = None
***************
*** 909,912 ****
--- 915,976 ----
SetWaitCursor(0)
+ def HandleNotification(self, disposition):
+ if self.config.notification.notify_sound_enabled:
+ if disposition == "Yes":
+ self.received_spam += 1
+ elif disposition == "No":
+ self.received_ham += 1
+ else:
+ self.received_unsure += 1
+ self._StartNotifyTimer()
+
+ def _StartNotifyTimer(self):
+ # First kill any existing timer
+ self._KillNotifyTimer()
+ # And start a new timer.
+ delay = self.config.notification.notify_accumulate_delay
+ self._DoStartNotifyTimer(delay)
+ pass
+
+ def _DoStartNotifyTimer(self, delay):
+ assert thread.get_ident() == self.owner_thread_ident
+ assert self.notify_timer_id is None, "Shouldn't start a timer when already have one"
+ assert type(delay)==type(0.0), "Timer values are float seconds"
+ # And start a new timer.
+ assert delay, "No delay means no timer!"
+ delay = int(delay*1000) # convert to ms.
+ self.notify_timer_id = timer.set_timer(delay, self._NotifyTimerFunc)
+ self.LogDebug(1, "Notify timer started - id=%d, delay=%d" % (self.notify_timer_id, delay))
+
+ def _KillNotifyTimer(self):
+ assert thread.get_ident() == self.owner_thread_ident
+ if self.notify_timer_id is not None:
+ timer.kill_timer(self.notify_timer_id)
+ self.LogDebug(2, "The notify timer with id=%d was stopped" % self.notify_timer_id)
+ self.notify_timer_id = None
+
+ def _NotifyTimerFunc(self, event, time):
+ # Kill the timer first
+ assert thread.get_ident() == self.owner_thread_ident
+ self.LogDebug(1, "The notify timer with id=%s fired" % self.notify_timer_id)
+ self._KillNotifyTimer()
+
+ import winsound
+ config = self.config.notification
+ sound_opts = winsound.SND_FILENAME | winsound.SND_ASYNC | winsound.SND_NOSTOP | winsound.SND_NODEFAULT
+ self.LogDebug(3, "Notify received ham=%d, unsure=%d, spam=%d" %
+ (self.received_ham, self.received_unsure, self.received_spam))
+ if self.received_ham > 0 and len(config.notify_ham_sound) > 0:
+ self.LogDebug(3, "Playing ham sound '%s'" % config.notify_ham_sound)
+ winsound.PlaySound(config.notify_ham_sound, sound_opts)
+ elif self.received_unsure > 0 and len(config.notify_unsure_sound) > 0:
+ self.LogDebug(3, "Playing unsure sound '%s'" % config.notify_unsure_sound)
+ winsound.PlaySound(config.notify_unsure_sound, sound_opts)
+ elif self.received_spam > 0 and len(config.notify_spam_sound) > 0:
+ self.LogDebug(3, "Playing spam sound '%s'" % config.notify_spam_sound)
+ winsound.PlaySound(config.notify_spam_sound, sound_opts)
+ # Reset received counts to zero after notify.
+ self.received_ham = self.received_unsure = self.received_spam = 0
+
_mgr = None
From anadelonbrin at users.sourceforge.net Mon Dec 6 00:28:53 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 6 00:28:55 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs dialog_map.py,
1.41, 1.42
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7228/Outlook2000/dialogs
Modified Files:
dialog_map.py
Log Message:
Fix [ 1078923 ] Unicode support incomplete
(Need to encode the data_directory when asking the OS to show it).
Index: dialog_map.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dialog_map.py,v
retrieving revision 1.41
retrieving revision 1.42
diff -C2 -d -r1.41 -r1.42
*** dialog_map.py 11 Nov 2004 21:55:40 -0000 1.41
--- dialog_map.py 5 Dec 2004 23:28:50 -0000 1.42
***************
*** 236,240 ****
"""
import os
! os.startfile(window.manager.data_directory)
def ShowLog(window):
"""Opens the log file for the current SpamBayes session
--- 236,242 ----
"""
import os
! import sys
! filesystem_encoding = sys.getfilesystemencoding()
! os.startfile(window.manager.data_directory.encode(filesystem_encoding))
def ShowLog(window):
"""Opens the log file for the current SpamBayes session
From anadelonbrin at users.sourceforge.net Mon Dec 6 01:11:35 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 6 01:11:37 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.90,1.91
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17895/Outlook2000
Modified Files:
msgstore.py
Log Message:
I'm not sure that PR_DISPLAY_NAME_A is the right property to find
the "From" name in. At least, I don't have any messages with
that property, and I get a lot of Exchange-only mail. So if
that property isn't found, use PR_SENDER_NAME_A if that can be found (which works
for me).
This will help those that need additional clues with Exchange-only messages.
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.90
retrieving revision 1.91
diff -C2 -d -r1.90 -r1.91
*** msgstore.py 26 Nov 2004 03:11:43 -0000 1.90
--- msgstore.py 6 Dec 2004 00:11:31 -0000 1.91
***************
*** 986,990 ****
# on an exchange server that do not have such headers of their own
prop_ids = PR_SUBJECT_A, PR_DISPLAY_NAME_A, PR_DISPLAY_TO_A, \
! PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME
hr, data = self.mapi_object.GetProps(prop_ids,0)
subject = self._GetPotentiallyLargeStringProp(prop_ids[0], data[0])
--- 986,991 ----
# on an exchange server that do not have such headers of their own
prop_ids = PR_SUBJECT_A, PR_DISPLAY_NAME_A, PR_DISPLAY_TO_A, \
! PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
! PR_SENDER_NAME_A
hr, data = self.mapi_object.GetProps(prop_ids,0)
subject = self._GetPotentiallyLargeStringProp(prop_ids[0], data[0])
***************
*** 993,1001 ****
cc = self._GetPotentiallyLargeStringProp(prop_ids[3], data[3])
delivery_time = data[4][1]
headers = ["X-Exchange-Message: true"]
! if subject: headers.append("Subject: "+subject)
! if sender: headers.append("From: "+sender)
! if to: headers.append("To: "+to)
! if cc: headers.append("CC: "+cc)
if delivery_time:
from time import timezone
--- 994,1010 ----
cc = self._GetPotentiallyLargeStringProp(prop_ids[3], data[3])
delivery_time = data[4][1]
+ alt_sender = self._GetPotentiallyLargeStringProp(prop_ids[5],
+ data[5])
headers = ["X-Exchange-Message: true"]
! if subject:
! headers.append("Subject: "+subject)
! if sender:
! headers.append("From: "+sender)
! elif alt_sender:
! headers.append("From: "+alt_sender)
! if to:
! headers.append("To: "+to)
! if cc:
! headers.append("CC: "+cc)
if delivery_time:
from time import timezone
From anadelonbrin at users.sourceforge.net Mon Dec 6 02:37:51 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 6 02:37:58 2004
Subject: [Spambayes-checkins] spambayes/spambayes Dibbler.py,1.14,1.15
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3159/spambayes
Modified Files:
Dibbler.py
Log Message:
Fix the regex that the auth digest used (+ was outside the group, so only the first
letter of the key would be used).
IE 6.0 and Firefox 1.0 appear to give back improper auth responses, based on my limited
understanding of the RFC, so make allowances for that. Auth digest works for me
now, at least.
(If anyone would like to check this, that would be great). This needs to be backported,
too (I'll do that later).
Index: Dibbler.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Dibbler.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -C2 -d -r1.14 -r1.15
*** Dibbler.py 19 Jul 2004 02:10:12 -0000 1.14
--- Dibbler.py 6 Dec 2004 01:37:43 -0000 1.15
***************
*** 347,351 ****
# RE to extract option="value" fields from
# digest auth login field
! _login_splitter = re.compile('([a-zA-Z])+=(".*?"|.*?),?')
def __init__(self, clientSocket, server, context):
--- 347,351 ----
# RE to extract option="value" fields from
# digest auth login field
! _login_splitter = re.compile('([a-zA-Z]+)=(".*?"|.*?),?')
def __init__(self, clientSocket, server, context):
***************
*** 631,634 ****
--- 631,640 ----
unhashedDigest = ""
if options.has_key("qop"):
+ # IE 6.0 doesn't give nc back correctly?
+ if not options["nc"]:
+ options["nc"] = "00000001"
+ # Firefox 1.0 doesn't give qop back correctly?
+ if not options["qop"]:
+ options["qop"] = "auth"
unhashedDigest = "%s:%s:%s:%s:%s:%s" % \
(HA1, nonce,
From anadelonbrin at users.sourceforge.net Mon Dec 6 04:04:19 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 6 04:04:22 2004
Subject: [Spambayes-checkins] spambayes setup.py,1.31,1.32
Message-ID:
Update of /cvsroot/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21289
Modified Files:
setup.py
Log Message:
Fix bug found by Barry.
Don't re-use global variable names!
Index: setup.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/setup.py,v
retrieving revision 1.31
retrieving revision 1.32
diff -C2 -d -r1.31 -r1.32
*** setup.py 30 Nov 2004 23:00:11 -0000 1.31
--- setup.py 6 Dec 2004 03:04:17 -0000 1.32
***************
*** 75,85 ****
import distutils.command.sdist
! parent = distutils.command.sdist.sdist
! class sdist(parent):
"""Like the standard sdist, but also prints out MD5 checksums and sizes
for the created files, for convenience."""
def run(self):
import md5
! retval = parent.run(self)
for archive in self.get_archive_files():
data = file(archive, "rb").read()
--- 75,85 ----
import distutils.command.sdist
! sdist_parent = distutils.command.sdist.sdist
! class sdist(sdist_parent):
"""Like the standard sdist, but also prints out MD5 checksums and sizes
for the created files, for convenience."""
def run(self):
import md5
! retval = sdist_parent.run(self)
for archive in self.get_archive_files():
data = file(archive, "rb").read()
From kpitt at users.sourceforge.net Mon Dec 6 18:50:07 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Mon Dec 6 18:50:12 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 oastats.py,1.8,1.9
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8330
Modified Files:
oastats.py
Log Message:
More detailed statistics in SpamBayes Manager. These roughly match the
updated statistics in the sb_server Web UI.
Index: oastats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/oastats.py,v
retrieving revision 1.8
retrieving revision 1.9
diff -C2 -d -r1.8 -r1.9
*** oastats.py 2 Nov 2004 21:33:46 -0000 1.8
--- oastats.py 6 Dec 2004 17:50:04 -0000 1.9
***************
*** 85,88 ****
--- 85,90 ----
points.
"""
+ chunks = []
+ push = chunks.append
num_seen = self.num_ham + self.num_spam + self.num_unsure
if not session_only:
***************
*** 90,97 ****
num_seen += (totals["num_ham"] + totals["num_spam"] +
totals["num_unsure"])
if num_seen==0:
! return [_("SpamBayes has processed zero messages")]
! chunks = []
! push = chunks.append
if session_only:
num_ham = self.num_ham
--- 92,98 ----
num_seen += (totals["num_ham"] + totals["num_spam"] +
totals["num_unsure"])
+ push(_("Messages classified: %d") % num_seen);
if num_seen==0:
! return chunks
if session_only:
num_ham = self.num_ham
***************
*** 117,120 ****
--- 118,160 ----
perc_spam = 100.0 * num_spam / num_seen
perc_unsure = 100.0 * num_unsure / num_seen
+ num_ham_correct = num_ham - num_deleted_spam_fn
+ num_spam_correct = num_spam - num_recovered_good_fp
+ num_correct = num_ham_correct + num_spam_correct
+ num_incorrect = num_deleted_spam_fn + num_recovered_good_fp
+ perc_correct = 100.0 * num_correct / num_seen
+ perc_incorrect = 100.0 * num_incorrect / num_seen
+ perc_fp = 100.0 * num_recovered_good_fp / num_seen
+ perc_fn = 100.0 * num_deleted_spam_fn / num_seen
+ num_unsure_trained_ham = num_recovered_good - num_recovered_good_fp
+ num_unsure_trained_spam = num_deleted_spam - num_deleted_spam_fn
+ num_unsure_not_trained = num_unsure - num_unsure_trained_ham - num_unsure_trained_spam
+ if num_unsure:
+ perc_unsure_trained_ham = 100.0 * num_unsure_trained_ham / num_unsure
+ perc_unsure_trained_spam = 100.0 * num_unsure_trained_spam / num_unsure
+ perc_unsure_not_trained = 100.0 * num_unsure_not_trained / num_unsure
+ else:
+ perc_unsure_trained_ham = 0.0
+ perc_unsure_trained_spam = 0.0
+ perc_unsure_not_trained = 0.0
+ total_ham = num_ham_correct + num_recovered_good
+ total_spam = num_spam_correct + num_deleted_spam
+ if total_ham:
+ perc_ham_incorrect = 100.0 * num_recovered_good_fp / total_ham
+ perc_ham_unsure = 100.0 * num_unsure_trained_ham / total_ham
+ perc_ham_incorrect_or_unsure = \
+ 100.0 * (num_recovered_good_fp + num_unsure_trained_ham) / total_ham
+ else:
+ perc_ham_incorrect = 0.0
+ perc_ham_unsure = 0.0
+ perc_ham_incorrect_or_unsure = 0.0
+ if total_spam:
+ perc_spam_correct = 100.0 * num_spam_correct / total_spam
+ perc_spam_unsure = 100.0 * num_unsure_trained_spam / total_spam
+ perc_spam_correct_or_unsure = \
+ 100.0 * (num_spam_correct + num_unsure_trained_spam) / total_spam
+ else:
+ perc_spam_correct = 100.0
+ perc_spam_unsure = 0.0
+ perc_spam_incorrect_or_unsure = 100.0
format_dict = locals().copy()
del format_dict["self"]
***************
*** 122,126 ****
del format_dict["chunks"]
format_dict.update(dict(perc_spam=perc_spam, perc_ham=perc_ham,
! perc_unsure=perc_unsure, num_seen=num_seen))
format_dict["perc_ham_s"] = "%%(perc_ham).%df%%(perc)s" \
% (decimal_points,)
--- 162,181 ----
del format_dict["chunks"]
format_dict.update(dict(perc_spam=perc_spam, perc_ham=perc_ham,
! perc_unsure=perc_unsure, num_seen=num_seen,
! num_correct=num_correct, num_incorrect=num_incorrect,
! perc_correct=perc_correct, perc_incorrect=perc_incorrect,
! perc_fp=perc_fp, perc_fn=perc_fn,
! num_unsure_trained_ham=num_unsure_trained_ham,
! num_unsure_trained_spam=num_unsure_trained_spam,
! num_unsure_not_trained=num_unsure_not_trained,
! perc_unsure_trained_ham=perc_unsure_trained_ham,
! perc_unsure_trained_spam=perc_unsure_trained_spam,
! perc_unsure_not_trained=perc_unsure_not_trained,
! perc_ham_incorrect=perc_ham_incorrect,
! perc_ham_unsure=perc_ham_unsure,
! perc_ham_incorrect_or_unsure=perc_ham_incorrect_or_unsure,
! perc_spam_correct=perc_spam_correct,
! perc_spam_unsure=perc_spam_unsure,
! perc_spam_correct_or_unsure=perc_spam_correct_or_unsure))
format_dict["perc_ham_s"] = "%%(perc_ham).%df%%(perc)s" \
% (decimal_points,)
***************
*** 129,151 ****
format_dict["perc_unsure_s"] = "%%(perc_unsure).%df%%(perc)s" \
% (decimal_points,)
format_dict["perc"] = "%"
! push((_("SpamBayes has processed %(num_seen)d messages - " \
! "%(num_ham)d (%(perc_ham_s)s) good, " \
! "%(num_spam)d (%(perc_spam_s)s) spam " \
! "and %(num_unsure)d (%(perc_unsure_s)s) unsure") \
% format_dict) % format_dict)
- if num_recovered_good:
- push(_("%(num_recovered_good)d message(s) were manually " \
- "classified as good (with %(num_recovered_good_fp)d " \
- "being false positives)") % format_dict)
- else:
- push(_("No messages were manually classified as good"))
- if num_deleted_spam:
- push(_("%(num_deleted_spam)d message(s) were manually " \
- "classified as spam (with %(num_deleted_spam_fn)d " \
- "being false negatives)") % format_dict)
- else:
- push(_("No messages were manually classified as spam"))
return chunks
--- 184,252 ----
format_dict["perc_unsure_s"] = "%%(perc_unsure).%df%%(perc)s" \
% (decimal_points,)
+ format_dict["perc_correct_s"] = "%%(perc_correct).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_incorrect_s"] = "%%(perc_incorrect).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_fp_s"] = "%%(perc_fp).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_fn_s"] = "%%(perc_fn).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_spam_correct_s"] = "%%(perc_spam_correct).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_spam_unsure_s"] = "%%(perc_spam_unsure).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_spam_correct_or_unsure_s"] = "%%(perc_spam_correct_or_unsure).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_ham_incorrect_s"] = "%%(perc_ham_incorrect).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_ham_unsure_s"] = "%%(perc_ham_unsure).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_ham_incorrect_or_unsure_s"] = "%%(perc_ham_incorrect_or_unsure).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_unsure_trained_ham_s"] = "%%(perc_unsure_trained_ham).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_unsure_trained_spam_s"] = "%%(perc_unsure_trained_spam).%df%%(perc)s" \
+ % (decimal_points,)
+ format_dict["perc_unsure_not_trained_s"] = "%%(perc_unsure_not_trained).%df%%(perc)s" \
+ % (decimal_points,)
format_dict["perc"] = "%"
!
! push((_("\tGood:\t%(num_ham)d (%(perc_ham_s)s)") \
! % format_dict) % format_dict)
! push((_("\tSpam:\t%(num_spam)d (%(perc_spam_s)s)") \
! % format_dict) % format_dict)
! push((_("\tUnsure:\t%(num_unsure)d (%(perc_unsure_s)s)") \
! % format_dict) % format_dict)
! push("")
!
! push((_("Classified correctly:\t%(num_correct)d (%(perc_correct_s)s of total)") \
! % format_dict) % format_dict)
! push((_("Classified incorrectly:\t%(num_incorrect)d (%(perc_incorrect_s)s of total)") \
! % format_dict) % format_dict)
! if num_incorrect:
! push((_("\tFalse positives:\t%(num_recovered_good_fp)d (%(perc_fp_s)s of total)") \
! % format_dict) % format_dict)
! push((_("\tFalse negatives:\t%(num_deleted_spam_fn)d (%(perc_fn_s)s of total)") \
! % format_dict) % format_dict)
! push("")
!
! push(_("Manually classified as good:\t%(num_recovered_good)d") % format_dict)
! push(_("Manually classified as spam:\t%(num_deleted_spam)d") % format_dict)
! push("")
!
! if num_unsure:
! push((_("Unsures trained as good:\t%(num_unsure_trained_ham)d (%(perc_unsure_trained_ham_s)s of unsures)") \
! % format_dict) % format_dict)
! push((_("Unsures trained as spam:\t%(num_unsure_trained_spam)d (%(perc_unsure_trained_spam_s)s of unsures)") \
! % format_dict) % format_dict)
! push((_("Unsures not trained:\t\t%(num_unsure_not_trained)d (%(perc_unsure_not_trained_s)s of unsures)") \
! % format_dict) % format_dict)
! push("")
!
! push((_("Spam correctly identified:\t%(perc_spam_correct_s)s (+ %(perc_spam_unsure_s)s unsure)") \
! % format_dict) % format_dict)
! push((_("Ham incorrectly identified:\t%(perc_ham_incorrect_s)s (+ %(perc_ham_unsure_s)s unsure)") \
% format_dict) % format_dict)
return chunks
From kpitt at users.sourceforge.net Mon Dec 6 19:04:38 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Mon Dec 6 19:04:42 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py, 1.141,
1.142 oastats.py, 1.9, 1.10
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11566
Modified Files:
addin.py oastats.py
Log Message:
If I logged out of Windows without closing Outlook first, updated stats were
not saved. Saving the stats in the OnClose event of the explorer window
seems to fix this. To be safe, I left the Store() call in the
OnDisconnection event of the addin as well, and added a check in the Store()
routine so that stats are not saved again if they haven't changed.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.141
retrieving revision 1.142
diff -C2 -d -r1.141 -r1.142
*** addin.py 3 Dec 2004 21:43:19 -0000 1.141
--- addin.py 6 Dec 2004 18:04:34 -0000 1.142
***************
*** 1137,1144 ****
def OnClose(self):
! self.manager.LogDebug(3, "OnClose", self)
self.explorers_collection._DoDeadExplorer(self)
self.explorers_collection = None
self.toolbar = None
self.close() # disconnect events.
--- 1137,1145 ----
def OnClose(self):
! self.manager.LogDebug(3, "Explorer window closing", self)
self.explorers_collection._DoDeadExplorer(self)
self.explorers_collection = None
self.toolbar = None
+ self.manager.stats.Store() # save stats
self.close() # disconnect events.
Index: oastats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/oastats.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -C2 -d -r1.9 -r1.10
*** oastats.py 6 Dec 2004 17:50:04 -0000 1.9
--- oastats.py 6 Dec 2004 18:04:35 -0000 1.10
***************
*** 39,49 ****
def Store(self):
# Update totals, and save that.
for stat in ["num_ham", "num_spam", "num_unsure",
"num_deleted_spam", "num_deleted_spam_fn",
"num_recovered_good", "num_recovered_good_fp",]:
! self.totals[stat] += getattr(self, stat)
! store = open(self.stored_statistics_fn, 'wb')
! pickle.dump(self.totals, store)
! store.close()
# Reset, or the reporting for the remainder of this session will be
# incorrect.
--- 39,55 ----
def Store(self):
# Update totals, and save that.
+ should_store = False
for stat in ["num_ham", "num_spam", "num_unsure",
"num_deleted_spam", "num_deleted_spam_fn",
"num_recovered_good", "num_recovered_good_fp",]:
! count = getattr(self, stat)
! self.totals[stat] += count
! if count != 0:
! # One of the totals changed, so we need to store the updates.
! should_store = True
! if should_store:
! store = open(self.stored_statistics_fn, 'wb')
! pickle.dump(self.totals, store)
! store.close()
# Reset, or the reporting for the remainder of this session will be
# incorrect.
From anadelonbrin at users.sourceforge.net Tue Dec 7 01:43:52 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Tue Dec 7 01:43:54 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 oastats.py,1.10,1.11
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7727/Outlook2000
Modified Files:
oastats.py
Log Message:
For historical reasons we use "good" not "ham"; make consistent.
Fix a typo that stopped the stats working if there was no spam trained.
Index: oastats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/oastats.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** oastats.py 6 Dec 2004 18:04:35 -0000 1.10
--- oastats.py 7 Dec 2004 00:43:49 -0000 1.11
***************
*** 162,166 ****
perc_spam_correct = 100.0
perc_spam_unsure = 0.0
! perc_spam_incorrect_or_unsure = 100.0
format_dict = locals().copy()
del format_dict["self"]
--- 162,166 ----
perc_spam_correct = 100.0
perc_spam_unsure = 0.0
! perc_spam_correct_or_unsure = 100.0
format_dict = locals().copy()
del format_dict["self"]
***************
*** 252,256 ****
push((_("Spam correctly identified:\t%(perc_spam_correct_s)s (+ %(perc_spam_unsure_s)s unsure)") \
% format_dict) % format_dict)
! push((_("Ham incorrectly identified:\t%(perc_ham_incorrect_s)s (+ %(perc_ham_unsure_s)s unsure)") \
% format_dict) % format_dict)
--- 252,256 ----
push((_("Spam correctly identified:\t%(perc_spam_correct_s)s (+ %(perc_spam_unsure_s)s unsure)") \
% format_dict) % format_dict)
! push((_("Good incorrectly identified:\t%(perc_ham_incorrect_s)s (+ %(perc_ham_unsure_s)s unsure)") \
% format_dict) % format_dict)
From anadelonbrin at users.sourceforge.net Wed Dec 8 03:02:35 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 8 03:02:38 2004
Subject: [Spambayes-checkins] spambayes/spambayes/test test_storage.py, 1.6,
1.7
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10490/spambayes/test
Modified Files:
test_storage.py
Log Message:
Add a simple store and load test.
Add a test to check that nham and nspam are correctly adjusted.
Simplify tearDown.
Replace a convoluted sys.stderr redirection with assertRaises.
Add cases for ZODBClassifier and CDBClassifier.
Skip the dbm tests if no dbm modules are available, and skip the ZODB tests if ZODB
is not available (informing the user in each case).
Index: test_storage.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_storage.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** test_storage.py 9 Jul 2004 03:24:48 -0000 1.6
--- test_storage.py 8 Dec 2004 02:02:32 -0000 1.7
***************
*** 8,11 ****
--- 8,12 ----
sb_test_support.fix_sys_path()
+ from spambayes.storage import ZODBClassifier, CDBClassifier
from spambayes.storage import DBDictClassifier, PickledClassifier
***************
*** 19,26 ****
--- 20,68 ----
def tearDown(self):
+ self.classifier.close()
self.classifier = None
if os.path.isfile(self.db_name):
os.remove(self.db_name)
+ def testLoadAndStore(self):
+ # Simple test to verify that putting data in the db, storing and
+ # then loading gives back the same data.
+ c = self.classifier
+ c.learn(["some", "simple", "tokens"], True)
+ c.learn(["some", "other"], False)
+ c.learn(["ones"], False)
+ c.store()
+ c.close()
+ del self.classifier
+ self.classifier = self.StorageClass(self.db_name)
+ self._checkAllWordCounts((("some", 1, 1),
+ ("simple", 0, 1),
+ ("tokens", 0, 1),
+ ("other", 1, 0),
+ ("ones", 1, 0)), False)
+ self.assertEqual(self.classifier.nham, 2)
+ self.assertEqual(self.classifier.nspam, 1)
+
+ def testCounts(self):
+ # Check that nham and nspam are correctedly adjusted.
+ c = self.classifier
+ count = 30
+ for i in xrange(count):
+ c.learn(["tony"], True)
+ self.assertEqual(c.nspam, i+1)
+ self.assertEqual(c.nham, 0)
+ for i in xrange(count):
+ c.learn(["tony"], False)
+ self.assertEqual(c.nham, i+1)
+ self.assertEqual(c.nspam, count)
+ for i in xrange(count):
+ c.unlearn(["tony"], True)
+ self.assertEqual(c.nham, count)
+ self.assertEqual(c.nspam, count-i-1)
+ for i in xrange(count):
+ c.unlearn(["tony"], False)
+ self.assertEqual(c.nham, count-i-1)
+ self.assertEqual(c.nspam, 0)
+
def _checkWordCounts(self, word, expected_ham, expected_spam):
assert word
***************
*** 131,138 ****
StorageClass = DBDictClassifier
- def tearDown(self):
- self.classifier.db.close()
- _StorageTestBase.tearDown(self)
-
def _fail_open_best(self, *args):
from spambayes import dbmstorage
--- 173,176 ----
***************
*** 146,172 ****
DBDictClassifier_load = DBDictClassifier.load
DBDictClassifier.load = self._fail_open_best
! # Redirect sys.stderr, as open_storage() prints a msg to stderr.
! # Then it does sys.exit(), which we catch.
! sys_stderr = sys.stderr
! sys.stderr = StringIO.StringIO()
try:
! try:
! open_storage(db_name, "dbm")
! except SystemExit:
! pass
! else:
! self.fail("expected SystemExit from open_storage() call")
finally:
DBDictClassifier.load = DBDictClassifier_load
- sys.stderr = sys_stderr
if os.path.isfile(db_name):
os.remove(db_name)
def suite():
suite = unittest.TestSuite()
! for cls in (PickleStorageTestCase,
! DBStorageTestCase,
! ):
suite.addTest(unittest.makeSuite(cls))
return suite
--- 184,236 ----
DBDictClassifier_load = DBDictClassifier.load
DBDictClassifier.load = self._fail_open_best
! print "This test will print out an error, which can be ignored."
try:
! self.assertRaises(SystemExit, open_storage, (db_name, "dbm"))
finally:
DBDictClassifier.load = DBDictClassifier_load
if os.path.isfile(db_name):
os.remove(db_name)
+ class CDBStorageTestCase(_StorageTestBase):
+ StorageClass = CDBClassifier
+
+ class ZODBStorageTestCase(_StorageTestBase):
+ StorageClass = ZODBClassifier
+
def suite():
suite = unittest.TestSuite()
! clses = (PickleStorageTestCase,
! CDBStorageTestCase,
! )
! try:
! import gdbm
! except ImportError:
! gdbm = None
! if sys.platform != "win32" or sys.version_info > (2,3):
! try:
! import bsddb
! except ImportError:
! bsddb = None
! else:
! bsddb = None
! try:
! import bsddb3
! except ImportError:
! bsddb3 = None
!
! if gdbm or bsddb or bsddb3:
! clses += (DBStorageTestCase,)
! else:
! print "Skipping dbm tests, no dbm module available"
!
! try:
! import ZODB
! except ImportError:
! print "Skipping ZODB tests, ZODB not available"
! else:
! clses += (ZODBStorageTestCase,)
!
! for cls in clses:
suite.addTest(unittest.makeSuite(cls))
return suite
From anadelonbrin at users.sourceforge.net Wed Dec 8 03:06:28 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 8 03:06:30 2004
Subject: [Spambayes-checkins] spambayes/spambayes storage.py,1.44,1.45
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11398/spambayes
Modified Files:
storage.py
Log Message:
Fix an import.
Let ZODB handle nham and nspam for us.
Avoid endless recursive AttributeErrors.
Print out (to stderr) the same messages that the other storage types do.
Ensure that we don't try to load a closed ZODB database.
Do a store before closing to avoid problems.
The unittests for ZODBClassifier now pass, and the database lasts more than one Outlook
session, so I think it is somewhat usable :)
Index: storage.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/storage.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -C2 -d -r1.44 -r1.45
*** storage.py 22 Nov 2004 00:26:44 -0000 1.44
--- storage.py 8 Dec 2004 02:06:25 -0000 1.45
***************
*** 662,667 ****
# is ok.
try:
! Persistent
! except NameError:
Persistent = object
class _PersistentClassifier(classifier.Classifier, Persistent):
--- 662,667 ----
# is ok.
try:
! from persistent import Persistent
! except ImportError:
Persistent = object
class _PersistentClassifier(classifier.Classifier, Persistent):
***************
*** 675,685 ****
class ZODBClassifier(object):
def __init__(self, db_name):
- self.statekey = STATE_KEY
self.db_name = db_name
self.load()
def __getattr__(self, att):
# We pretend that we are a classifier subclass.
! if hasattr(self.classifier, att):
return getattr(self.classifier, att)
raise AttributeError("ZODBClassifier object has no attribute '%s'"
--- 675,685 ----
class ZODBClassifier(object):
def __init__(self, db_name):
self.db_name = db_name
+ self.closed = True
self.load()
def __getattr__(self, att):
# We pretend that we are a classifier subclass.
! if hasattr(self, "classifier") and hasattr(self.classifier, att):
return getattr(self.classifier, att)
raise AttributeError("ZODBClassifier object has no attribute '%s'"
***************
*** 699,706 ****
def load(self):
import ZODB
self.create_storage()
self.db = ZODB.DB(self.storage)
! root = self.db.open().root()
self.classifier = root.get(self.db_name)
if self.classifier is None:
--- 699,717 ----
def load(self):
+ '''Load state from database'''
import ZODB
+
+ if options["globals", "verbose"]:
+ print >> sys.stderr, 'Loading state from', self.db_name, 'database'
+
+ # If we are not closed, then we need to close first before we
+ # reload.
+ if not self.closed:
+ self.close()
+
self.create_storage()
self.db = ZODB.DB(self.storage)
! self.conn = self.db.open()
! root = self.conn.root()
self.classifier = root.get(self.db_name)
if self.classifier is None:
***************
*** 709,735 ****
print >> sys.stderr, self.db_name, 'is a new ZODB'
self.classifier = root[self.db_name] = _PersistentClassifier()
- get_transaction().commit()
else:
- # It seems to me that the persistent classifier should store
- # the nham and nspam values, but that doesn't appear to be the
- # case, so work around that. This can be removed once I figure
- # out the problem.
- self.nham, self.nspam = self.classifier.wordinfo[self.statekey]
if options["globals", "verbose"]:
print >> sys.stderr, '%s is an existing ZODB, with %d ' \
'ham and %d spam' % (self.db_name, self.nham,
self.nspam)
def store(self):
! # It seems to me that the persistent classifier should store
! # the nham and nspam values, but that doesn't appear to be the
! # case, so work around that. This can be removed once I figure
! # out the problem.
! self.classifier.wordinfo[self.statekey] = (self.nham, self.nspam)
! get_transaction().commit()
def close(self):
self.db.close()
self.storage.close()
--- 720,756 ----
print >> sys.stderr, self.db_name, 'is a new ZODB'
self.classifier = root[self.db_name] = _PersistentClassifier()
else:
if options["globals", "verbose"]:
print >> sys.stderr, '%s is an existing ZODB, with %d ' \
'ham and %d spam' % (self.db_name, self.nham,
self.nspam)
+ self.closed = False
def store(self):
! '''Place state into persistent store'''
! import ZODB
! import transaction
!
! assert self.closed == False, "Can't store a closed database"
!
! if options["globals", "verbose"]:
! print >> sys.stderr, 'Persisting', self.db_name, 'state in database'
!
! transaction.commit()
def close(self):
+ # Ensure that the db is saved before closing. Alternatively, we
+ # could abort any waiting transaction. We need to do *something*
+ # with it, though, or it will be still around after the db is
+ # closed and cause problems. For now, saving seems to make sense
+ # (and we can always add abort methods if they are ever needed).
+ self.store()
+
+ # Do the closing.
self.db.close()
self.storage.close()
+ self.closed = True
+ if options["globals", "verbose"]:
+ print >> sys.stderr, 'Closed', self.db_name, 'database'
From anadelonbrin at users.sourceforge.net Wed Dec 8 04:31:38 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 8 04:31:41 2004
Subject: [Spambayes-checkins] spambayes/spambayes/test test_message.py, 1.1,
1.2
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29996/spambayes/test
Modified Files:
test_message.py
Log Message:
Add Python 2.2 compatibility.
Add tests for delNotations.
Add a test for inserting an exception header in a message without a header/body separator.
Index: test_message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_message.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** test_message.py 22 Nov 2004 00:22:55 -0000 1.1
--- test_message.py 8 Dec 2004 03:31:35 -0000 1.2
***************
*** 21,25 ****
# Richie's hammer.py script has code for generating any number of
# randomly composed email messages.
! from test_sb_server import good1, spam1
TEMP_PICKLE_NAME = os.path.join(os.path.dirname(__file__), "temp.pik")
--- 21,31 ----
# Richie's hammer.py script has code for generating any number of
# randomly composed email messages.
! from test_sb_server import good1, spam1, malformed1
!
! try:
! __file__
! except NameError:
! # Python 2.2 compatibility.
! __file__ = sys.argv[0]
TEMP_PICKLE_NAME = os.path.join(os.path.dirname(__file__), "temp.pik")
***************
*** 453,456 ****
--- 459,522 ----
self.assert_(header not in self.msg.keys())
+ def test_delNotations(self):
+ # Add each type of notation to each header and check that it
+ # is removed.
+ for headername in ["subject", "to"]:
+ for disp in (self.ham, self.spam, self.unsure):
+ # Add a notation to the header
+ header = self.msg[headername]
+ self.assert_(disp not in header)
+ options["Headers", "notate_%s" % (headername,)] = \
+ (self.ham, self.unsure, self.spam)
+ prob = {self.ham:self.g_prob, self.spam:self.s_prob,
+ self.unsure:self.u_prob}[disp]
+ self.msg.addSBHeaders(prob, self.clues)
+ self.assert_(disp in self.msg[headername])
+ # Remove it
+ self.msg.delNotations()
+ self.assertEqual(self.msg[headername], header)
+
+ def test_delNotations_missing(self):
+ # Check that nothing is removed if the disposition is not
+ # there.
+ for headername in ["subject", "to"]:
+ for disp in (self.ham, self.spam, self.unsure):
+ # Add a notation to the header
+ header = self.msg[headername]
+ self.assert_(disp not in header)
+ options["Headers", "notate_%s" % (headername,)] = ()
+ prob = {self.ham:self.g_prob, self.spam:self.s_prob,
+ self.unsure:self.u_prob}[disp]
+ self.msg.addSBHeaders(prob, self.clues)
+ self.assert_(disp not in self.msg[headername])
+ # Remove it
+ self.msg.delNotations()
+ self.assertEqual(self.msg[headername], header)
+
+ def test_delNotations_only_once(self):
+ # Check that only one disposition is removed, even if more than
+ # one is present.
+ for headername in ["subject", "to"]:
+ for disp in (self.ham, self.spam, self.unsure):
+ # Add a notation to the header
+ header = self.msg[headername]
+ self.assert_(disp not in header)
+ options["Headers", "notate_%s" % (headername,)] = \
+ (self.ham, self.unsure, self.spam)
+ prob = {self.ham:self.g_prob, self.spam:self.s_prob,
+ self.unsure:self.u_prob}[disp]
+ self.msg.addSBHeaders(prob, self.clues)
+ self.assert_(disp in self.msg[headername])
+ header2 = self.msg[headername]
+ # Add a second notation
+ self.msg.addSBHeaders(prob, self.clues)
+ self.assert_(disp in
+ self.msg[headername].replace(disp, "", 1))
+ # Remove it
+ self.msg.delNotations()
+ self.assertEqual(self.msg[headername], header2)
+ # Restore for next time round the loop
+ self.msg.replace_header(headername, header)
+
class MessageInfoBaseTest(unittest.TestCase):
***************
*** 607,612 ****
headerName = 'X-Spambayes-Exception'
header = email.Header.Header(details, header_name=headerName)
! self.assertEqual(msg[headerName].replace('\n', '\r\n'),
! str(header).replace('\n', '\r\n'))
def test_insert_exception_header(self):
--- 673,678 ----
headerName = 'X-Spambayes-Exception'
header = email.Header.Header(details, header_name=headerName)
! self.assertEqual(msg[headerName].replace('\r\n', '\n'),
! str(header).replace('\r\n', '\n'))
def test_insert_exception_header(self):
***************
*** 635,638 ****
--- 701,714 ----
header = email.Header.Header(id, header_name=headerName)
self.assertEqual(msg[headerName], str(header).replace('\n', '\r\n'))
+
+ def test_insert_exception_header_no_separator(self):
+ # Cause an exception to insert.
+ try:
+ raise Exception("Test")
+ except Exception:
+ pass
+ msg, details = insert_exception_header(malformed1)
+ self._verify_details(details)
+ self._verify_exception_header(msg, details)
From anadelonbrin at users.sourceforge.net Wed Dec 8 04:34:32 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 8 04:34:35 2004
Subject: [Spambayes-checkins] spambayes/spambayes ProxyUI.py, 1.53,
1.54 message.py, 1.60, 1.61
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30428/spambayes
Modified Files:
ProxyUI.py message.py
Log Message:
Don't overwrite existing attributes even if the message isn't already in the database.
Add a function to remove (if present) the subject/to notations. Do so when removing
all SB headers, as this is symmetric with adding and is typically what we want (we
usually remove in order to train, and don't want them present then).
Remove notations before adding messages to the review page.
Adds [ 848365 ] Remove subject annotations from message review page
Index: ProxyUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v
retrieving revision 1.53
retrieving revision 1.54
diff -C2 -d -r1.53 -r1.54
*** ProxyUI.py 28 Nov 2004 23:38:17 -0000 1.53
--- ProxyUI.py 8 Dec 2004 03:34:29 -0000 1.54
***************
*** 681,685 ****
bodySummary and other header (as needed) attributes. These objects
are passed into appendMessages by onReview - passing email.Message
! objects directly uses too much memory."""
subjectHeader = message["Subject"] or "(none)"
headers = {"subject" : subjectHeader}
--- 681,689 ----
bodySummary and other header (as needed) attributes. These objects
are passed into appendMessages by onReview - passing email.Message
! objects directly uses too much memory.
! """
! # Remove notations before displaying - see:
! # [ 848365 ] Remove subject annotations from message review page
! message.delNotations()
subjectHeader = message["Subject"] or "(none)"
headers = {"subject" : subjectHeader}
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.60
retrieving revision 1.61
diff -C2 -d -r1.60 -r1.61
*** message.py 29 Nov 2004 00:17:59 -0000 1.60
--- message.py 8 Dec 2004 03:34:29 -0000 1.61
***************
*** 132,136 ****
# Set to None, as it's not there.
for att in msg.stored_attributes:
! setattr(msg, att, None)
else:
if not isinstance(attributes, types.ListType):
--- 132,138 ----
# Set to None, as it's not there.
for att in msg.stored_attributes:
! # Don't overwrite.
! if not hasattr(msg, att):
! setattr(msg, att, None)
else:
if not isinstance(attributes, types.ListType):
***************
*** 452,458 ****
self[headerName] = "".join(wrappedEvd)
! # These are pretty ugly, but no-one has a better idea about how to
! # allow filtering in 'stripped down' mailers like Outlook Express,
! # so for the moment, they stay in.
# options["Headers", "notate_to"] (and notate_subject) can be
# either a single string (like "spam") or a tuple (like
--- 454,471 ----
self[headerName] = "".join(wrappedEvd)
! if options['Headers','add_unique_id']:
! self[options['Headers','mailid_header_name']] = self.id
!
! self.addNotations()
!
! def addNotations(self):
! """Add the appropriate string to the subject: and/or to: header.
!
! This is a reasonably ugly method of including the classification,
! but no-one has a better idea about how to allow filtering in
! 'stripped down' mailers (i.e. Outlook Express), so, for the moment,
! this is it.
! """
! disposition = self.GetClassification()
# options["Headers", "notate_to"] (and notate_subject) can be
# either a single string (like "spam") or a tuple (like
***************
*** 488,493 ****
self["Subject"] = disposition
! if options['Headers','add_unique_id']:
! self[options['Headers','mailid_header_name']] = self.id
def currentSBHeaders(self):
--- 501,540 ----
self["Subject"] = disposition
! def delNotations(self):
! """If present, remove our notation from the subject: and/or to:
! header of the message.
!
! This is somewhat problematic, as we cannot be 100% positive that we
! added the notation. It's almost certain to be us with the to:
! header, but someone else might have played with the subject:
! header. However, as long as the user doesn't turn this option on
! and off, this will all work nicely.
!
! See also [ 848365 ] Remove subject annotations from message review
! page
! """
! subject = self["Subject"]
! ham = options["Headers", "header_ham_string"] + ','
! spam = options["Headers", "header_spam_string"] + ','
! unsure = options["Headers", "header_unsure_string"] + ','
! if options["Headers", "notate_subject"]:
! for disp in (ham, spam, unsure):
! if subject.startswith(disp):
! self.replace_header("Subject", subject[len(disp):])
! # Only remove one, maximum.
! break
! to = self["To"]
! ham = "%s@spambayes.invalid;" % \
! (options["Headers", "header_ham_string"],)
! spam = "%s@spambayes.invalid;" % \
! (options["Headers", "header_spam_string"],)
! unsure = "%s@spambayes.invalid;" % \
! (options["Headers", "header_unsure_string"],)
! if options["Headers", "notate_to"]:
! for disp in (ham, spam, unsure):
! if to.startswith(disp):
! self.replace_header("To", to[len(disp):])
! # Only remove one, maximum.
! break
def currentSBHeaders(self):
***************
*** 517,520 ****
--- 564,570 ----
del self[options['Headers','score_header_name']]
del self[options['Headers','trained_header_name']]
+ # Also delete notations - typically this is called just before
+ # training, and we don't want them there for that.
+ self.delNotations()
# Utility function to insert an exception header into the given RFC822 text.
From anadelonbrin at users.sourceforge.net Wed Dec 8 05:28:01 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 8 05:28:04 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 config.py, 1.34,
1.35 filter.py, 1.40, 1.41
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7519/Outlook2000
Modified Files:
config.py filter.py
Log Message:
Add [ 1036970 ] Allow Outlook plugin to move ham to a designated folder
This can't be configured via the GUI at the moment, but does at least provide the
capability. If we end up making more room in the Manager dialog before 1.1 goes
out, then adding configuration for this would be reasonable to do, as long as it
doesn't end up looking too complicated.
Index: config.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/config.py,v
retrieving revision 1.34
retrieving revision 1.35
diff -C2 -d -r1.34 -r1.35
*** config.py 3 Dec 2004 21:43:19 -0000 1.34
--- config.py 8 Dec 2004 04:27:59 -0000 1.35
***************
*** 188,192 ****
will be considered spam, and processed accordingly."""),
REAL, RESTORE),
! ("spam_action", _("The action to take for new spam"), "Moved",
_("""The action that should be taken as Spam messages arrive."""),
FILTER_ACTION, RESTORE),
--- 188,192 ----
will be considered spam, and processed accordingly."""),
REAL, RESTORE),
! ("spam_action", _("The action to take for new spam"), FILTER_ACTION[1],
_("""The action that should be taken as Spam messages arrive."""),
FILTER_ACTION, RESTORE),
***************
*** 215,218 ****
--- 215,229 ----
filtered. See 'spam_mark_as_read' for more details."""),
BOOLEAN, RESTORE),
+ (FolderIDOption,
+ "ham_folder_id", _("The folder to which good messages are moved"), None,
+ _("""The folder SpamBayes moves or copies good messages to."""),
+ FOLDER_ID, DO_NOT_RESTORE),
+ ("ham_action", _("The action to take for new good messages"), FILTER_ACTION[0],
+ _("""The action that should be taken as good messages arrive."""),
+ FILTER_ACTION, RESTORE),
+ ("ham_mark_as_read", _("Should filtered good message also be marked as 'read'"), False,
+ _("""Determines if good messages are marked as 'Read' as they are
+ filtered. See 'spam_mark_as_read' for more details."""),
+ BOOLEAN, RESTORE),
("enabled", _("Is filtering enabled?"), False,
_(""""""),
Index: filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
retrieving revision 1.40
retrieving revision 1.41
diff -C2 -d -r1.40 -r1.41
*** filter.py 26 Nov 2004 03:11:43 -0000 1.40
--- filter.py 8 Dec 2004 04:27:59 -0000 1.41
***************
*** 22,26 ****
else:
disposition = "No"
! attr_prefix = None
ms = mgr.message_store
--- 22,26 ----
else:
disposition = "No"
! attr_prefix = "ham"
ms = mgr.message_store
From kpitt at users.sourceforge.net Wed Dec 15 20:46:33 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Wed Dec 15 20:46:36 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.91,1.92
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13348/Outlook2000
Modified Files:
msgstore.py
Log Message:
Include Message-ID in fake Exchange headers.
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.91
retrieving revision 1.92
diff -C2 -d -r1.91 -r1.92
*** msgstore.py 6 Dec 2004 00:11:31 -0000 1.91
--- msgstore.py 15 Dec 2004 19:46:28 -0000 1.92
***************
*** 29,32 ****
--- 29,33 ----
MYPR_BODY_HTML_A = 0x1013001e # magic
MYPR_BODY_HTML_W = 0x1013001f # ditto
+ MYPR_MESSAGE_ID_A = 0x1035001E # more magic (message id field used for Exchange)
CLEAR_READ_FLAG = 0x00000004
***************
*** 987,991 ****
prop_ids = PR_SUBJECT_A, PR_DISPLAY_NAME_A, PR_DISPLAY_TO_A, \
PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
! PR_SENDER_NAME_A
hr, data = self.mapi_object.GetProps(prop_ids,0)
subject = self._GetPotentiallyLargeStringProp(prop_ids[0], data[0])
--- 988,992 ----
prop_ids = PR_SUBJECT_A, PR_DISPLAY_NAME_A, PR_DISPLAY_TO_A, \
PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
! PR_SENDER_NAME_A, MYPR_MESSAGE_ID_A
hr, data = self.mapi_object.GetProps(prop_ids,0)
subject = self._GetPotentiallyLargeStringProp(prop_ids[0], data[0])
***************
*** 996,999 ****
--- 997,1002 ----
alt_sender = self._GetPotentiallyLargeStringProp(prop_ids[5],
data[5])
+ message_id = self._GetPotentiallyLargeStringProp(prop_ids[6],
+ data[6])
headers = ["X-Exchange-Message: true"]
if subject:
***************
*** 1007,1010 ****
--- 1010,1015 ----
if cc:
headers.append("CC: "+cc)
+ if message_id:
+ headers.append("Message-ID: "+message_id)
if delivery_time:
from time import timezone
From anadelonbrin at users.sourceforge.net Thu Dec 16 04:23:19 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 16 04:23:22 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.92,1.93
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17493/Outlook2000
Modified Files:
msgstore.py
Log Message:
Improve the faked up exchange headers that we generate if there are no
Internet headers.
Address headers are now in a standard format.
Generate organisation, importance and date.
Change X-Exchange-Delivery-Time to received.
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.92
retrieving revision 1.93
diff -C2 -d -r1.92 -r1.93
*** msgstore.py 15 Dec 2004 19:46:28 -0000 1.92
--- msgstore.py 16 Dec 2004 03:23:16 -0000 1.93
***************
*** 30,33 ****
--- 30,35 ----
MYPR_BODY_HTML_W = 0x1013001f # ditto
MYPR_MESSAGE_ID_A = 0x1035001E # more magic (message id field used for Exchange)
+ MYPR_VERSION_ID = 0x7E8FFFFD # magic that I think tells us the Outlook version
+ MYPR_ORGANISATION_A = 0x1037001E # I think this is the organisation magic
CLEAR_READ_FLAG = 0x00000004
***************
*** 985,1022 ****
def _GetFakeHeaders(self):
# This is designed to fake up some SMTP headers for messages
! # on an exchange server that do not have such headers of their own
! prop_ids = PR_SUBJECT_A, PR_DISPLAY_NAME_A, PR_DISPLAY_TO_A, \
PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
! PR_SENDER_NAME_A, MYPR_MESSAGE_ID_A
! hr, data = self.mapi_object.GetProps(prop_ids,0)
! subject = self._GetPotentiallyLargeStringProp(prop_ids[0], data[0])
! sender = self._GetPotentiallyLargeStringProp(prop_ids[1], data[1])
! to = self._GetPotentiallyLargeStringProp(prop_ids[2], data[2])
! cc = self._GetPotentiallyLargeStringProp(prop_ids[3], data[3])
! delivery_time = data[4][1]
! alt_sender = self._GetPotentiallyLargeStringProp(prop_ids[5],
! data[5])
! message_id = self._GetPotentiallyLargeStringProp(prop_ids[6],
! data[6])
headers = ["X-Exchange-Message: true"]
! if subject:
! headers.append("Subject: "+subject)
! if sender:
! headers.append("From: "+sender)
! elif alt_sender:
! headers.append("From: "+alt_sender)
! if to:
! headers.append("To: "+to)
! if cc:
! headers.append("CC: "+cc)
! if message_id:
! headers.append("Message-ID: "+message_id)
! if delivery_time:
! from time import timezone
! from email.Utils import formatdate
! headers.append("X-Exchange-Delivery-Time: "+\
! formatdate(int(delivery_time)-timezone, True))
return "\n".join(headers) + "\n"
def _EnsureObject(self):
if self.mapi_object is None:
--- 987,1050 ----
def _GetFakeHeaders(self):
# This is designed to fake up some SMTP headers for messages
! # on an exchange server that do not have such headers of their own.
! prop_ids = PR_SUBJECT_A, PR_SENDER_NAME_A, PR_DISPLAY_TO_A, \
PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
! MYPR_MESSAGE_ID_A, PR_IMPORTANCE, PR_CLIENT_SUBMIT_TIME, \
! MYPR_ORGANISATION_A,
! # This property gives a 'The parameter is incorrect' error, for some
! # reason, as does, 0x7E8EFFE2, which I think is the 'pretty' version
! # number. Until that's figured out, we'll have to not get this.
! # MYPR_VERSION_ID
! hr, data = self.mapi_object.GetProps(prop_ids, 0)
headers = ["X-Exchange-Message: true"]
! for header, index, potentially_large, format_func in (\
! ("Subject", 0, True, None),
! ("From", 1, True, self._format_address),
! ("To", 2, True, self._format_address),
! ("CC", 3, True, self._format_address),
! ("Received", 4, False, self._format_received),
! ("Message-ID", 5, True, None),
! ("Importance", 6, False, self._format_importance),
! ("Date", 7, False, self._format_time),
! ("Organisation", 8, True, None),
! # ("X-Mailer", 9, False, self._format_version),
! ):
! if potentially_large:
! value = self._GetPotentiallyLargeStringProp(prop_ids[index],
! data[index])
! else:
! value = data[index][1]
! if value:
! if format_func:
! value = format_func(value)
! headers.append("%s: %s" % (header, value))
return "\n".join(headers) + "\n"
+ def _format_received(self, raw):
+ # Fake up a 'received' header. It's important that the date
+ # is right, so that sort+group.py will work. The rest is just more
+ # clues for the tokenizer to find.
+ return "(via local Exchange server); %s" % (self._format_time(raw),)
+
+ def _format_time(self, raw):
+ from time import timezone
+ from email.Utils import formatdate
+ return formatdate(int(raw)-timezone, True)
+
+ def _format_importance(self, raw):
+ # olImportanceHigh = 2, olImportanceLow = 0, olImportanceNormal = 1
+ return {0 : "low", 1 : "normal", 2 : "high"}[raw]
+
+ def _format_version(self, raw):
+ # Data is just a version string, so prepend something to it.
+ return "Exchange Client " + raw
+
+ _address_re = re.compile(r"[()<>,:@!/=; ]")
+ def _format_address(self, raw):
+ # Fudge up something that's in the appropriate form. We don't
+ # have enough information available to get an actual working
+ # email address.
+ return "%s@invalid (%s)" % (self._address_re.sub('', raw), raw)
+
def _EnsureObject(self):
if self.mapi_object is None:
From anadelonbrin at users.sourceforge.net Thu Dec 16 06:31:22 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 16 06:31:25 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_imapfilter.py,1.45,1.46
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4711/scripts
Modified Files:
sb_imapfilter.py
Log Message:
Compile re's and do this only once. This should speed things up (previously we regenerated
the re each time (eg) we retrieved a message.
Safeguard getting the RFC822.Header data, as this is causing problems at the moment.
Print out additional debugging material for the moment, and possibly handle this
better later, when we know more about what is going wrong.
Index: sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v
retrieving revision 1.45
retrieving revision 1.46
diff -C2 -d -r1.45 -r1.46
*** sb_imapfilter.py 30 Nov 2004 23:49:23 -0000 1.45
--- sb_imapfilter.py 16 Dec 2004 05:31:02 -0000 1.46
***************
*** 131,135 ****
# There's a tricky situation where if use_ssl is False, but we
# try to connect to a IMAP over SSL server, we will just hang
! # forever, waiting for a repsonse that will never come. To
# get past this, just for the welcome message, we install a
# timeout on the connection. Normal service is then returned.
--- 131,135 ----
# There's a tricky situation where if use_ssl is False, but we
# try to connect to a IMAP over SSL server, we will just hang
! # forever, waiting for a response that will never come. To
# get past this, just for the welcome message, we install a
# timeout on the connection. Normal service is then returned.
***************
*** 256,259 ****
--- 256,261 ----
return data
+ number_re = re.compile(r"{\d+}")
+ folder_re = re.compile(r"\(([\w\\ ]*)\) ")
def folder_list(self):
"""Return a alphabetical list of all folders available on the
***************
*** 272,282 ****
# literal, so we need to crunch this out.
if isinstance(fol, types.TupleType):
! m = re.search(r"{\d+}", fol[0])
if not m:
# Something is wrong here! Skip this folder.
continue
fol = '%s"%s"' % (fol[0][:m.start()], fol[1])
! r = re.compile(r"\(([\w\\ ]*)\) ")
! m = r.search(fol)
if not m:
# Something is not good with this folder, so skip it.
--- 274,283 ----
# literal, so we need to crunch this out.
if isinstance(fol, types.TupleType):
! m = self.number_re.search(fol[0])
if not m:
# Something is wrong here! Skip this folder.
continue
fol = '%s"%s"' % (fol[0][:m.start()], fol[1])
! m = self.folder_re.search(fol)
if not m:
# Something is not good with this folder, so skip it.
***************
*** 511,514 ****
--- 512,516 ----
return message.SBHeaderMessage.as_string(self, unixfrom)
+ recent_re = re.compile(r"\\Recent ?| ?\\Recent")
def Save(self):
"""Save message to IMAP server.
***************
*** 537,541 ****
# The \Recent flag can be fetched, but cannot be stored
# We must remove it from the list if it is there.
! flags = re.sub(r"\\Recent ?| ?\\Recent", "", flags)
else:
flags = None
--- 539,543 ----
# The \Recent flag can be fetched, but cannot be stored
# We must remove it from the list if it is there.
! flags = self.recent_re.sub("", flags)
else:
flags = None
***************
*** 676,679 ****
--- 678,686 ----
return []
+ custom_header_id_re = re.compile(re.escape(\
+ options["Headers", "mailid_header_name"]) + "\:\s*(\d+(?:\-\d)?)",
+ re.IGNORECASE)
+ message_id_re = re.compile("Message-ID\: ?\<([^\n\>]+)\>",
+ re.IGNORECASE)
def __getitem__(self, key):
"""Return message matching the given *uid*.
***************
*** 688,694 ****
# that not all servers accept it, even though it is in the RFC
response = self.imap_server.uid("FETCH", key, "RFC822.HEADER")
! data = self.imap_server.check_response("fetch %s rfc822.header" \
! % (key,), response)
! data = self.imap_server.extract_fetch_data(data[0])
# Create a new IMAPMessage object, which will be the return value.
--- 695,701 ----
# that not all servers accept it, even though it is in the RFC
response = self.imap_server.uid("FETCH", key, "RFC822.HEADER")
! response_data = self.imap_server.check_response(\
! "fetch %s rfc822.header" % (key,), response)
! data = self.imap_server.extract_fetch_data(response_data[0])
# Create a new IMAPMessage object, which will be the return value.
***************
*** 700,709 ****
# We use the MessageID header as the ID for the message, as long
# as it is available, and if not, we add our own.
! custom_header_id = re.escape(options["Headers",
! "mailid_header_name"]) + \
! "\:\s*(\d+(?:\-\d)?)"
# Search for our custom id first, for backwards compatibility.
! for id_header in [custom_header_id, "Message-ID\: ?\<([^\n\>]+)\>"]:
! mo = re.search(id_header, data["RFC822.HEADER"], re.IGNORECASE)
if mo:
msg.setId(mo.group(1))
--- 707,728 ----
# We use the MessageID header as the ID for the message, as long
# as it is available, and if not, we add our own.
! try:
! headers = data["RFC822.HEADER"]
! except KeyError:
! # This is bad! We asked for this in the fetch, so either
! # our parsing is wrong or the response from the server is
! # wrong. For the moment, print out some debugging info
! # and don't do anything else (which means we will keep
! # coming back to this message).
! print >> sys.stderr, "Trouble parsing response:", \
! response_data, data
! print >> sys.stderr, "Please report this to spambayes@python.org"
! if options["globals", "verbose"]:
! sys.stdout.write("?")
! return msg
!
# Search for our custom id first, for backwards compatibility.
! for id_header_re in [self.custom_header_id_re, self.message_id_re]:
! mo = id_header_re.search(headers)
if mo:
msg.setId(mo.group(1))
From kpitt at users.sourceforge.net Thu Dec 16 17:20:25 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 16 17:20:29 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.93,1.94
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4936
Modified Files:
msgstore.py
Log Message:
Improve e-mail address formatting for fake Exchange headers so that it
supports multiple addresses, some of which may be real Internet addresses.
Also, RFC 822 uses the American spelling of "Organization".
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.93
retrieving revision 1.94
diff -C2 -d -r1.93 -r1.94
*** msgstore.py 16 Dec 2004 03:23:16 -0000 1.93
--- msgstore.py 16 Dec 2004 16:20:23 -0000 1.94
***************
*** 1007,1011 ****
("Importance", 6, False, self._format_importance),
("Date", 7, False, self._format_time),
! ("Organisation", 8, True, None),
# ("X-Mailer", 9, False, self._format_version),
):
--- 1007,1011 ----
("Importance", 6, False, self._format_importance),
("Date", 7, False, self._format_time),
! ("Organization", 8, True, None),
# ("X-Mailer", 9, False, self._format_version),
):
***************
*** 1045,1049 ****
# have enough information available to get an actual working
# email address.
! return "%s@invalid (%s)" % (self._address_re.sub('', raw), raw)
def _EnsureObject(self):
--- 1045,1059 ----
# have enough information available to get an actual working
# email address.
! addresses = raw.split(";")
! formattedAddresses = []
! for address in addresses:
! address = address.strip()
! if address.find("@") >= 0:
! formattedAddress = address
! else:
! formattedAddress = "\"%s\" <%s>" % \
! (address, self._address_re.sub('.', address))
! formattedAddresses.append(formattedAddress)
! return "; ".join(formattedAddresses)
def _EnsureObject(self):
From kpitt at users.sourceforge.net Thu Dec 16 18:43:54 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 16 18:43:58 2004
Subject: [Spambayes-checkins] spambayes WHAT_IS_NEW.txt,1.35,1.36
Message-ID:
Update of /cvsroot/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24402
Modified Files:
WHAT_IS_NEW.txt
Log Message:
The use_bigrams option is no longer experimental.
Index: WHAT_IS_NEW.txt
===================================================================
RCS file: /cvsroot/spambayes/spambayes/WHAT_IS_NEW.txt,v
retrieving revision 1.35
retrieving revision 1.36
diff -C2 -d -r1.35 -r1.36
*** WHAT_IS_NEW.txt 5 May 2004 00:37:07 -0000 1.35
--- WHAT_IS_NEW.txt 16 Dec 2004 17:43:50 -0000 1.36
***************
*** 144,154 ****
for more details).
- o [Classifier] x-use_bigrams
- By default, SpamBayes uses unigrams tokens that are basically
- single words (split on whitespace). This option enables both unigrams
- and bigrams (pairs of words), but uses a 'tiling' scheme, where only
- the set of unigrams and bigrams that have the strongest effect on
- the message are used.
-
o [URLRetriever] x-slurp_urls
o [URLRetriever] x-cache_expiry_days
--- 144,147 ----
From anadelonbrin at users.sourceforge.net Fri Dec 17 01:37:24 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Fri Dec 17 01:37:27 2004
Subject: [Spambayes-checkins]
spambayes/spambayes/test test_sb_imapfilter.py, 1.6, 1.7
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20003/spambayes/test
Modified Files:
test_sb_imapfilter.py
Log Message:
Update to reflect the new API for extract_fetch_data.
Add a new, more difficult test, that matches the problem outlined on spambayes@python.org.
Index: test_sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_sb_imapfilter.py,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** test_sb_imapfilter.py 22 Nov 2004 00:22:55 -0000 1.6
--- test_sb_imapfilter.py 17 Dec 2004 00:37:21 -0000 1.7
***************
*** 400,438 ****
message_number = "123"
uid = "5432"
! response = "%s (UID %s)" % (message_number, uid)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data["message_number"], message_number)
! self.assertEqual(data["UID"], uid)
# Check INTERNALDATE, FLAGS.
flags = r"(\Seen \Deleted)"
date = '"27-Jul-2004 13:11:56 +1200"'
! response = "%s (FLAGS %s INTERNALDATE %s)" % \
! (message_number, flags, date)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data["FLAGS"], flags)
! self.assertEqual(data["INTERNALDATE"], date)
# Check RFC822 and literals.
rfc = "Subject: Test\r\n\r\nThis is a test message."
! response = ("%s (RFC822 {%s}" % (message_number, len(rfc)), rfc)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data["message_number"], message_number)
! self.assertEqual(data["RFC822"], rfc)
# Check RFC822.HEADER.
headers = "Subject: Foo\r\nX-SpamBayes-ID: 1231-1\r\n"
! response = ("%s (RFC822.HEADER {%s}" % (message_number,
! len(headers)), headers)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data["RFC822.HEADER"], headers)
# Check BODY.PEEK.
peek = "Subject: Test2\r\n\r\nThis is another test message."
! response = ("%s (BODY[] {%s}" % (message_number, len(peek)),
! peek)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data["BODY[]"], peek)
class IMAPMessageTest(BaseIMAPFilterTest):
--- 400,496 ----
message_number = "123"
uid = "5432"
! response = ("%s (UID %s)" % (message_number, uid),)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data[message_number]["message_number"],
! message_number)
! self.assertEqual(data[message_number]["UID"], uid)
# Check INTERNALDATE, FLAGS.
flags = r"(\Seen \Deleted)"
date = '"27-Jul-2004 13:11:56 +1200"'
! response = ("%s (FLAGS %s INTERNALDATE %s)" % \
! (message_number, flags, date),)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data[message_number]["message_number"],
! message_number)
! self.assertEqual(data[message_number]["FLAGS"], flags)
! self.assertEqual(data[message_number]["INTERNALDATE"], date)
# Check RFC822 and literals.
rfc = "Subject: Test\r\n\r\nThis is a test message."
! response = (("%s (RFC822 {%s}" % (message_number, len(rfc)), rfc),)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data[message_number]["message_number"],
! message_number)
! self.assertEqual(data[message_number]["RFC822"], rfc)
# Check RFC822.HEADER.
headers = "Subject: Foo\r\nX-SpamBayes-ID: 1231-1\r\n"
! response = (("%s (RFC822.HEADER {%s}" % (message_number,
! len(headers)), headers),)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data[message_number]["RFC822.HEADER"], headers)
# Check BODY.PEEK.
peek = "Subject: Test2\r\n\r\nThis is another test message."
! response = (("%s (BODY[] {%s}" % (message_number, len(peek)),
! peek),)
data = self.imap.extract_fetch_data(response)
! self.assertEqual(data[message_number]["BODY[]"], peek)
+ # A more complcated test with more than one message number.
+ uid = '3018'
+ flags = '(\\Seen \\Deleted)'
+ headers = "Return-Path: \r\nX-Original-To" \
+ ": david@leinbach.name\r\nDelivered-To: dleinbac@ma" \
+ "il2.majro.dhs.org\r\nReceived: from its-mail1.mass" \
+ "ey.ac.nz (its-mail1.massey.ac.nz [130.123.128.11])" \
+ "\r\n\tby mail2.majro.dhs.org (Postfix) with ESMTP " \
+ "id 7BC5018FE22\r\n\tfor ; Mon" \
+ ", 13 Dec 2004 22:46:05 -0800 (PST)\r\nReceived: fr" \
+ "om its-mm1.massey.ac.nz (its-mm1 [130.123.128.45])" \
+ "\r\n\tby its-mail1.massey.ac.nz (8.9.3/8.9.3) with" \
+ "ESMTP id TAA12081;\r\n\tTue, 14 Dec 2004 19:45:56 " \
+ "+1300 (NZDT)\r\nReceived: from its-campus2.massey." \
+ "ac.nz (Not Verified[130.123.48.254]) by its-mm1.ma" \
+ "ssey.ac.nz with NetIQ MailMarshal\r\n\tid ; Tue, 14 Dec 2004 19:45:56 +1300\r\nReceived:" \
+ "from it029048 (it029048.massey.ac.nz [130.123.238." \
+ "51])\r\n\tby its-campus2.massey.ac.nz (8.9.3/8.9.3" \
+ ") with ESMTP id TAA05881;\r\n\tTue, 14 Dec 2004 19" \
+ ':45:55 +1300 (NZDT)\r\nFrom: "Tony Meyer" \r\nTo: "\'David Leinbach\'" , \r\nSubject: " \
+ "RE: [Spambayes] KeyError in sp_imapfilter\r\nDate:" \
+ "Tue, 14 Dec 2004 19:45:25 +1300\r\nMessage-ID: \r\nMIME-Version: 1.0\r\nContent-Type: t" \
+ 'ext/plain;\r\n\tcharset="us-ascii"\r\nContent-Tran' \
+ "sfer-Encoding: quoted-printable\r\nX-Priority: 3 (" \
+ "Normal)\r\nX-MSMail-Priority: Normal\r\nX-Mailer: " \
+ "Microsoft Outlook, Build 10.0.4510\r\nIn-Reply-To:" \
+ "\r\nX-Habeas-SWE-1: winter into spri" \
+ "ng\r\nX-Habeas-SWE-2: brightly anticipated\r\nX-Ha" \
+ "beas-SWE-3: like Habeas SWE (tm)\r\nX-Habeas-SWE-4" \
+ ": Copyright 2002 Habeas (tm)\r\nX-Habeas-SWE-5: Se" \
+ "nder Warranted Email (SWE) (tm). The sender of thi" \
+ "s\r\nX-Habeas-SWE-6: email in exchange for a licen" \
+ "se for this Habeas\r\nX-Habeas-SWE-7: warrant mark" \
+ "warrants that this is a Habeas Compliant\r\nX-Habe" \
+ "as-SWE-8: Message (HCM) and not spam. Please repo" \
+ "rt use of this\r\nX-Habeas-SWE-9: mark in spam to " \
+ ".\r\nX-MimeOLE: Pro" \
+ "duced By Microsoft MimeOLE V6.00.2900.2180\r\nImpo" \
+ "rtance: Normal\r\n\r\n"
+ response = ['1 (FLAGS %s)' % flags,
+ ('2 (UID %s RFC822.HEADER {%d}' % (uid, len(headers)),
+ headers), ')']
+ data = self.imap.extract_fetch_data(response)
+ self.assertEqual(data['1']["message_number"], '1')
+ self.assertEqual(data['2']["message_number"], '2')
+ self.assertEqual(data['1']["FLAGS"], flags)
+ self.assertEqual(data['2']["UID"], uid)
+ self.assertEqual(data['2']["RFC822.HEADER"], headers)
class IMAPMessageTest(BaseIMAPFilterTest):
From anadelonbrin at users.sourceforge.net Fri Dec 17 01:38:47 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Fri Dec 17 01:38:50 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_imapfilter.py,1.46,1.47
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20378/scripts
Modified Files:
sb_imapfilter.py
Log Message:
Update extract_fetch_data to handle any number of literals/message numbers. A dict
of dicts is now returned.
Index: sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v
retrieving revision 1.46
retrieving revision 1.47
diff -C2 -d -r1.46 -r1.47
*** sb_imapfilter.py 16 Dec 2004 05:31:02 -0000 1.46
--- sb_imapfilter.py 17 Dec 2004 00:38:45 -0000 1.47
***************
*** 320,348 ****
re.escape(FLAG_CHARS) + r"\"\{\}\(\)\\ ]*)\)?")
LITERAL_RE = re.compile(r"^\{[\d]+\}$")
! def extract_fetch_data(self, response):
! """Extract data from the response given to an IMAP FETCH command.
!
! The data is put into a dictionary, which is returned, where the
! keys are the fetch items.
"""
- # The response might be a tuple containing literal data.
- # At the moment, we only handle one literal per response. This
- # may need to be improved if our code ever asks for something
- # more complicated (like RFC822.Header and RFC822.Body).
- if isinstance(response, types.TupleType):
- literal = response[1]
- response = response[0]
- else:
- literal = None
-
- # The first item will always be the message number.
- mo = self.FETCH_RESPONSE_RE.match(response)
- data = {}
- if mo:
- data["message_number"] = mo.group(1)
- response = mo.group(2)
- else:
- raise BadIMAPResponseError("FETCH response", response)
-
# We support the following FETCH items:
# FLAGS
--- 320,327 ----
re.escape(FLAG_CHARS) + r"\"\{\}\(\)\\ ]*)\)?")
LITERAL_RE = re.compile(r"^\{[\d]+\}$")
! def _extract_fetch_data(self, response):
! """This does the real work of extracting the data, for each message
! number.
"""
# We support the following FETCH items:
# FLAGS
***************
*** 353,364 ****
# BODY.PEEK
# All others are ignored.
! for r in [self.FLAGS_RE, self.INTERNALDATE_RE, self.RFC822_RE,
! self.UID_RE, self.RFC822_HEADER_RE, self.BODY_PEEK_RE]:
! mo = r.search(response)
! if mo is not None:
! if self.LITERAL_RE.match(mo.group(2)):
! data[mo.group(1)] = literal
! else:
! data[mo.group(1)] = mo.group(2)
return data
--- 332,391 ----
# BODY.PEEK
# All others are ignored.
!
! if isinstance(response, types.StringTypes):
! response = (response,)
!
! data = {}
! expected_literal = None
! for part in response:
! # We ignore parentheses by themselves, for convenience.
! if part == ')':
! continue
! if expected_literal:
! # This should be a literal of a certain size.
! key, expected_size = expected_literal
! ## if len(part) != expected_size:
! ## raise BadIMAPResponseError(\
! ## "FETCH response (wrong size literal %d != %d)" % \
! ## (len(part), expected_size), response)
! data[key] = part
! expected_literal = None
! continue
! # The first item will always be the message number.
! mo = self.FETCH_RESPONSE_RE.match(part)
! if mo:
! data["message_number"] = mo.group(1)
! rest = mo.group(2)
! else:
! raise BadIMAPResponseError("FETCH response", response)
!
! for r in [self.FLAGS_RE, self.INTERNALDATE_RE, self.RFC822_RE,
! self.UID_RE, self.RFC822_HEADER_RE, self.BODY_PEEK_RE]:
! mo = r.search(rest)
! if mo is not None:
! if self.LITERAL_RE.match(mo.group(2)):
! # The next element will be a literal.
! expected_literal = (mo.group(1),
! int(mo.group(2)[1:-1]))
! else:
! data[mo.group(1)] = mo.group(2)
! return data
!
! def extract_fetch_data(self, response):
! """Extract data from the response given to an IMAP FETCH command.
!
! The data is put into a dictionary, which is returned, where the
! keys are the fetch items.
! """
! # There may be more than one message number in the response, so
! # handle separately.
! if isinstance(response, types.StringTypes):
! response = (response,)
!
! data = {}
! for msg in response:
! msg_data = self._extract_fetch_data(msg)
! if msg_data:
! data[msg_data["message_number"]] = msg_data
return data
***************
*** 448,457 ****
command = "uid fetch %s" % (self.uid,)
! data = self.imap_server.check_response(command, response)
! data = self.imap_server.extract_fetch_data(data[0])
try:
! new_msg = email.message_from_string(data[self.rfc822_key],
! IMAPMessage)
# We use a general 'except' because the email package doesn't
# always return email.Errors (it can return a TypeError, for
--- 475,494 ----
command = "uid fetch %s" % (self.uid,)
! response_data = self.imap_server.check_response(command, response)
! data = self.imap_server.extract_fetch_data(response_data)
! # The data will be a dictionary - hopefully with only one element,
! # but maybe more than one. The key is the message number, which we
! # do not have (we use the UID instead). So we look through the
! # message and use the first data of the right type we find.
! rfc822_data = None
! for msg_data in data.itervalues():
! if self.rfc822_key in msg_data:
! rfc822_data = msg_data[self.rfc822_key]
! break
! if rfc822_data is None:
! raise BadIMAPResponseError("FETCH response", response_data)
try:
! new_msg = email.message_from_string(rfc822_data, IMAPMessage)
# We use a general 'except' because the email package doesn't
# always return email.Errors (it can return a TypeError, for
***************
*** 471,475 ****
self.invalid = True
text, details = message.insert_exception_header(
! data[self.rfc822_key], self.id)
self.invalid_content = text
self.got_substance = True
--- 508,512 ----
self.invalid = True
text, details = message.insert_exception_header(
! rfc822_data, self.id)
self.invalid_content = text
self.got_substance = True
***************
*** 527,546 ****
"(FLAGS INTERNALDATE)")
command = "fetch %s (flags internaldate)" % (self.uid,)
! data = self.imap_server.check_response(command, response)
! data = self.imap_server.extract_fetch_data(data[0])
!
! if data.has_key("INTERNALDATE"):
! msg_time = data["INTERNALDATE"]
! else:
! msg_time = self.extractTime()
!
! if data.has_key("FLAGS"):
! flags = data["FLAGS"]
! # The \Recent flag can be fetched, but cannot be stored
! # We must remove it from the list if it is there.
! flags = self.recent_re.sub("", flags)
! else:
! flags = None
!
# We try to save with flags and time, then with just the
# time, then with the flags and the current time, then with just
--- 564,584 ----
"(FLAGS INTERNALDATE)")
command = "fetch %s (flags internaldate)" % (self.uid,)
! response_data = self.imap_server.check_response(command, response)
! data = self.imap_server.extract_fetch_data(response_data)
! # The data will be a dictionary - hopefully with only one element,
! # but maybe more than one. The key is the message number, which we
! # do not have (we use the UID instead). So we look through the
! # message and use the last data of the right type we find.
! msg_time = self.extractTime()
! flags = None
! for msg_data in data.itervalues():
! if "INTERNALDATE" in msg_data:
! msg_time = msg_data["INTERNALDATE"]
! if "FLAGS" in msg_data:
! flags = msg_data["FLAGS"]
! # The \Recent flag can be fetched, but cannot be stored
! # We must remove it from the list if it is there.
! flags = self.recent_re.sub("", flags)
!
# We try to save with flags and time, then with just the
# time, then with the flags and the current time, then with just
***************
*** 697,701 ****
response_data = self.imap_server.check_response(\
"fetch %s rfc822.header" % (key,), response)
! data = self.imap_server.extract_fetch_data(response_data[0])
# Create a new IMAPMessage object, which will be the return value.
--- 735,750 ----
response_data = self.imap_server.check_response(\
"fetch %s rfc822.header" % (key,), response)
! data = self.imap_server.extract_fetch_data(response_data)
! # The data will be a dictionary - hopefully with only one element,
! # but maybe more than one. The key is the message number, which we
! # do not have (we use the UID instead). So we look through the
! # message and use the first data of the right type we find.
! headers = None
! for msg_data in data.itervalues():
! if "RFC822.HEADER" in msg_data:
! headers = msg_data["RFC822.HEADER"]
! break
! if headers is None:
! raise BadIMAPResponseError("FETCH response", response_data)
# Create a new IMAPMessage object, which will be the return value.
***************
*** 707,725 ****
# We use the MessageID header as the ID for the message, as long
# as it is available, and if not, we add our own.
- try:
- headers = data["RFC822.HEADER"]
- except KeyError:
- # This is bad! We asked for this in the fetch, so either
- # our parsing is wrong or the response from the server is
- # wrong. For the moment, print out some debugging info
- # and don't do anything else (which means we will keep
- # coming back to this message).
- print >> sys.stderr, "Trouble parsing response:", \
- response_data, data
- print >> sys.stderr, "Please report this to spambayes@python.org"
- if options["globals", "verbose"]:
- sys.stdout.write("?")
- return msg
-
# Search for our custom id first, for backwards compatibility.
for id_header_re in [self.custom_header_id_re, self.message_id_re]:
--- 756,759 ----
From anadelonbrin at users.sourceforge.net Fri Dec 17 02:22:31 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Fri Dec 17 02:22:34 2004
Subject: [Spambayes-checkins] spambayes/spambayes/test test_message.py, 1.2,
1.3
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30892/spambayes/test
Modified Files:
test_message.py
Log Message:
The correct address separator is ',' not ';', so get notate_to to use that.
Index: test_message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_message.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** test_message.py 8 Dec 2004 03:31:35 -0000 1.2
--- test_message.py 17 Dec 2004 01:22:28 -0000 1.3
***************
*** 334,338 ****
options["Headers", "notate_to"] = (self.ham,)
self.msg.addSBHeaders(self.g_prob, self.clues)
! disp, orig = self.msg["To"].split(';', 1)
self.assertEqual(orig, self.to)
self.assertEqual(disp, "%s@spambayes.invalid" % (self.ham,))
--- 334,338 ----
options["Headers", "notate_to"] = (self.ham,)
self.msg.addSBHeaders(self.g_prob, self.clues)
! disp, orig = self.msg["To"].split(',', 1)
self.assertEqual(orig, self.to)
self.assertEqual(disp, "%s@spambayes.invalid" % (self.ham,))
***************
*** 341,345 ****
options["Headers", "notate_to"] = (self.ham, self.unsure)
self.msg.addSBHeaders(self.u_prob, self.clues)
! disp, orig = self.msg["To"].split(';', 1)
self.assertEqual(orig, self.to)
self.assertEqual(disp, "%s@spambayes.invalid" % (self.unsure,))
--- 341,345 ----
options["Headers", "notate_to"] = (self.ham, self.unsure)
self.msg.addSBHeaders(self.u_prob, self.clues)
! disp, orig = self.msg["To"].split(',', 1)
self.assertEqual(orig, self.to)
self.assertEqual(disp, "%s@spambayes.invalid" % (self.unsure,))
***************
*** 348,352 ****
options["Headers", "notate_to"] = (self.ham, self.spam, self.unsure)
self.msg.addSBHeaders(self.s_prob, self.clues)
! disp, orig = self.msg["To"].split(';', 1)
self.assertEqual(orig, self.to)
self.assertEqual(disp, "%s@spambayes.invalid" % (self.spam,))
--- 348,352 ----
options["Headers", "notate_to"] = (self.ham, self.spam, self.unsure)
self.msg.addSBHeaders(self.s_prob, self.clues)
! disp, orig = self.msg["To"].split(',', 1)
self.assertEqual(orig, self.to)
self.assertEqual(disp, "%s@spambayes.invalid" % (self.spam,))
***************
*** 399,403 ****
result = self.test_notate_to_ham()
# Just be sure that it's using the new value.
! self.assertEqual(self.msg["To"].split(';', 1)[0],
"bacon@spambayes.invalid")
finally:
--- 399,403 ----
result = self.test_notate_to_ham()
# Just be sure that it's using the new value.
! self.assertEqual(self.msg["To"].split(',', 1)[0],
"bacon@spambayes.invalid")
finally:
From anadelonbrin at users.sourceforge.net Fri Dec 17 02:23:39 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Fri Dec 17 02:23:42 2004
Subject: [Spambayes-checkins] spambayes/spambayes message.py,1.61,1.62
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31195/spambayes
Modified Files:
message.py
Log Message:
The correct address separator is ',' not ';', so get notate_to to use that.
Spit out a warning if the deprecated function is used.
Change the deprecated function to work in Python 2.4 (this bit should be backported).
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.61
retrieving revision 1.62
diff -C2 -d -r1.61 -r1.62
*** message.py 8 Dec 2004 03:34:29 -0000 1.61
--- message.py 17 Dec 2004 01:23:36 -0000 1.62
***************
*** 87,90 ****
--- 87,91 ----
import errno
import shelve
+ import warnings
try:
import cPickle as pickle
***************
*** 290,302 ****
This function does not work (as a result of using private
methods in a hackish way) in Python 2.4, so is now deprecated.
! Use *_from_string as described above."""
! prs = email.Parser.Parser()
! fp = StringIO.StringIO(payload)
! # this is kindof a hack, due to the fact that the parser creates a
! # new message object, and we already have the message object
! prs._parseheaders(self, fp)
! # we may want to do some header parsing error handling here
! # to try to extract important headers regardless of malformations
! prs._parsebody(self, fp)
def setId(self, id):
--- 291,307 ----
This function does not work (as a result of using private
methods in a hackish way) in Python 2.4, so is now deprecated.
! Use *_from_string as described above.
!
! More: Python 2.4 has a new email package, and the private functions
! are gone. So this won't even work. We have to do something to
! get this to work, for the 1.0.x branch, so use a different ugly
! hack.
! """
! warnings.warn("setPayload is deprecated. Use " \
! "email.message_from_string(payload, _class=" \
! "Message) instead.",
! DeprecationWarning, 2)
! new_me = email.message_from_string(payload, _class=Message)
! self.__dict__ = new_me.__dict__
def setId(self, id):
***************
*** 387,390 ****
--- 392,404 ----
'''Message class that is cognizant of SpamBayes headers.
Adds routines to add/remove headers for SpamBayes'''
+ def setPayload(self, payload):
+ """DEPRECATED.
+ """
+ warnings.warn("setPayload is deprecated. Use " \
+ "email.message_from_string(payload, _class=" \
+ "SBHeaderMessage) instead.",
+ DeprecationWarning, 2)
+ new_me = email.message_from_string(payload, _class=SBHeaderMessage)
+ self.__dict__ = new_me.__dict__
def setIdFromPayload(self):
***************
*** 486,490 ****
address = "%s@spambayes.invalid" % (disposition, )
try:
! self.replace_header("To", "%s;%s" % (address, self["To"]))
except KeyError:
self["To"] = address
--- 500,504 ----
address = "%s@spambayes.invalid" % (disposition, )
try:
! self.replace_header("To", "%s,%s" % (address, self["To"]))
except KeyError:
self["To"] = address
***************
*** 525,533 ****
break
to = self["To"]
! ham = "%s@spambayes.invalid;" % \
(options["Headers", "header_ham_string"],)
! spam = "%s@spambayes.invalid;" % \
(options["Headers", "header_spam_string"],)
! unsure = "%s@spambayes.invalid;" % \
(options["Headers", "header_unsure_string"],)
if options["Headers", "notate_to"]:
--- 539,547 ----
break
to = self["To"]
! ham = "%s@spambayes.invalid," % \
(options["Headers", "header_ham_string"],)
! spam = "%s@spambayes.invalid," % \
(options["Headers", "header_spam_string"],)
! unsure = "%s@spambayes.invalid," % \
(options["Headers", "header_unsure_string"],)
if options["Headers", "notate_to"]:
From anadelonbrin at users.sourceforge.net Fri Dec 17 02:37:07 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Fri Dec 17 02:37:09 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.94,1.95
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1913/Outlook2000
Modified Files:
msgstore.py
Log Message:
The organisation clue I found doesn't seem to be standard, so remove it.
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.94
retrieving revision 1.95
diff -C2 -d -r1.94 -r1.95
*** msgstore.py 16 Dec 2004 16:20:23 -0000 1.94
--- msgstore.py 17 Dec 2004 01:37:04 -0000 1.95
***************
*** 31,35 ****
MYPR_MESSAGE_ID_A = 0x1035001E # more magic (message id field used for Exchange)
MYPR_VERSION_ID = 0x7E8FFFFD # magic that I think tells us the Outlook version
- MYPR_ORGANISATION_A = 0x1037001E # I think this is the organisation magic
CLEAR_READ_FLAG = 0x00000004
--- 31,34 ----
***************
*** 991,995 ****
PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
MYPR_MESSAGE_ID_A, PR_IMPORTANCE, PR_CLIENT_SUBMIT_TIME, \
- MYPR_ORGANISATION_A,
# This property gives a 'The parameter is incorrect' error, for some
# reason, as does, 0x7E8EFFE2, which I think is the 'pretty' version
--- 990,993 ----
***************
*** 1007,1012 ****
("Importance", 6, False, self._format_importance),
("Date", 7, False, self._format_time),
! ("Organization", 8, True, None),
! # ("X-Mailer", 9, False, self._format_version),
):
if potentially_large:
--- 1005,1009 ----
("Importance", 6, False, self._format_importance),
("Date", 7, False, self._format_time),
! # ("X-Mailer", 8, False, self._format_version),
):
if potentially_large:
From anadelonbrin at users.sourceforge.net Mon Dec 20 03:49:09 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 03:49:13 2004
Subject: [Spambayes-checkins]
spambayes/spambayes/test test_sb_imapfilter.py, 1.7, 1.8
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv25987/spambayes/test
Modified Files:
test_sb_imapfilter.py
Log Message:
Add another test case - maybe the fetch data has multiple parts, but the message numbers
are the same.
Index: test_sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_sb_imapfilter.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -d -r1.7 -r1.8
*** test_sb_imapfilter.py 17 Dec 2004 00:37:21 -0000 1.7
--- test_sb_imapfilter.py 20 Dec 2004 02:49:06 -0000 1.8
***************
*** 494,497 ****
--- 494,533 ----
self.assertEqual(data['2']["RFC822.HEADER"], headers)
+ # Another complicated one (like the previous, but the two message
+ # numbers are the same).
+ headers = 'Return-Path: \r\n' \
+ 'X-Original-To: david@leinbach.name\r\n' \
+ 'Delivered-To: dleinbac@mail2.majro.dhs.org\r\n' \
+ 'Received: from cressida (unknown [70.70.206.137])' \
+ '\r\n\tby mail2.majro.dhs.org (Postfix) with ESMTP' \
+ 'id AAD451CD28E\r\n\tfor ; Mo' \
+ 'n, 6 Dec 2004 11:49:41 -0800 (PST)\r\n' \
+ 'From: "Erin Leinbach" \r\n' \
+ 'To: "Dave" \r\n' \
+ 'Subject: Goo goo dolls songs\r\n' \
+ 'Date: Mon, 6 Dec 2004 11:51:07 -0800\r\n' \
+ 'Message-ID: <000001c4dbcc$ed6188a0$6801a8c0@cre' \
+ 'ssida>\r\nMIME-Version: 1.0\r\n' \
+ 'Content-Type: text/plain;\r\n' \
+ '\tcharset="Windows-1252"\r\n' \
+ 'Content-Transfer-Encoding: 7bit\r\n' \
+ 'X-Priority: 3 (Normal)\r\n' \
+ 'X-MSMail-Priority: Normal\r\n' \
+ 'X-Mailer: Microsoft Outlook, Build 10.0.2616\r\n' \
+ 'Importance: Normal\r\n' \
+ 'X-MimeOLE: Produced By Microsoft MimeOLE V6.00.29' \
+ '00.2180\r\nX-Spambayes-Classification: ham\r\n' \
+ 'X-Spambayes-MailId: 000001\r\n\r\n'
+ uid = '3086'
+ flags = '(\\Seen \\Deleted)'
+ response = [('5 (UID %s RFC822.HEADER {839}' % (uid,), headers),
+ ')', '5 (FLAGS %s)' % (flags,)]
+ data = self.imap.extract_fetch_data(response)
+ self.assertEqual(data['5']["message_number"], '5')
+ self.assertEqual(data['5']["FLAGS"], flags)
+ self.assertEqual(data['5']["UID"], uid)
+ self.assertEqual(data['5']["RFC822.HEADER"], headers)
+
+
class IMAPMessageTest(BaseIMAPFilterTest):
def setUp(self):
From anadelonbrin at users.sourceforge.net Mon Dec 20 03:49:51 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 03:49:53 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_imapfilter.py,1.47,1.48
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26085/scripts
Modified Files:
sb_imapfilter.py
Log Message:
Maybe the fetch data has multiple parts, but the message numbers are the same. Handle
that correctly.
Index: sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v
retrieving revision 1.47
retrieving revision 1.48
diff -C2 -d -r1.47 -r1.48
*** sb_imapfilter.py 17 Dec 2004 00:38:45 -0000 1.47
--- sb_imapfilter.py 20 Dec 2004 02:49:47 -0000 1.48
***************
*** 387,391 ****
msg_data = self._extract_fetch_data(msg)
if msg_data:
! data[msg_data["message_number"]] = msg_data
return data
--- 387,396 ----
msg_data = self._extract_fetch_data(msg)
if msg_data:
! # Maybe there are two about the same message number!
! num = msg_data["message_number"]
! if num in data:
! data[num].update(msg_data)
! else:
! data[num] = msg_data
return data
From anadelonbrin at users.sourceforge.net Mon Dec 20 04:33:12 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 04:33:15 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/docs
troubleshooting.html, 1.24, 1.25
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/docs
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv675/Outlook2000/docs
Modified Files:
troubleshooting.html
Log Message:
Improve information about how to find the log.
Index: troubleshooting.html
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/docs/troubleshooting.html,v
retrieving revision 1.24
retrieving revision 1.25
diff -C2 -d -r1.24 -r1.25
*** troubleshooting.html 22 Oct 2004 00:06:44 -0000 1.24
--- troubleshooting.html 20 Dec 2004 03:33:08 -0000 1.25
***************
*** 287,292 ****
If you are running the source code version, then please see README.txt
in the Outlook2000 directory.
! If you are running the binary version, then the SpamBayes addin writes
! a log in your Windows temp directory. This directory is generally
\WINDOWS\TEMP for Windows 9x,
or \Documents and
--- 287,300 ----
If you are running the source code version, then please see README.txt
in the Outlook2000 directory.
! If you are running the binary version, the simplest way to get hold of
! the most recent log is to:
!
! - Open the SpamBayes Manager dialog (from the SpamBayes toolbar)
! - Click the Advanced tab.
! - Click the Diagnostics button.
! - Click the View log button.
!
! To find the log manually, you'll need to find your Windows temp directory,
! into which the SpamBayes addin writes the log. This directory is generally
\WINDOWS\TEMP for Windows 9x,
or \Documents and
From anadelonbrin at users.sourceforge.net Mon Dec 20 04:37:47 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 04:37:51 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.100,1.101
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1418/Outlook2000
Modified Files:
manager.py
Log Message:
Allow Outlook users to select their storage method in exactly the same way as other
users can. This isn't exposed via the GUI, so few will notice, and just about everyone
will continue on with the default (bsddb at the moment, still).
Also add a wrapper for the ZODB (FileStorage) storage class. I've been using this
for a while now, and it seems to work fine.
Remove some checks that no longer need to be done (that the messageinfo db has the
same length as the tokens db). This isn't the case now that we store info about
classified messages, and was only to track down a (solved) problem, anyway.
Index: manager.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
retrieving revision 1.100
retrieving revision 1.101
diff -C2 -d -r1.100 -r1.101
*** manager.py 3 Dec 2004 21:43:19 -0000 1.100
--- manager.py 20 Dec 2004 03:37:38 -0000 1.101
***************
*** 81,100 ****
this_filename = os.path.abspath(sys.argv[0])
# See if we can use the new bsddb module. (The old one is unreliable
# on Windows, so we don't use that)
! try:
! import bsddb3 as bsddb
! # bsddb3 is definitely not broken
! use_db = True
! except ImportError:
! # Not using the 3rd party bsddb3, so try the one in the std library
try:
import bsddb
- use_db = hasattr(bsddb, "db") # This name is not in the old one.
except ImportError:
! # No DB library at all!
! assert not hasattr(sys, "frozen"), \
! "Don't build binary versions without bsddb!"
! use_db = False
# This is a little bit of a hack . We are generally in a child
--- 81,102 ----
this_filename = os.path.abspath(sys.argv[0])
+ # Ensure that a bsddb module is available if we are frozen.
# See if we can use the new bsddb module. (The old one is unreliable
# on Windows, so we don't use that)
! if hasattr(sys, "frozen"):
! try:
! import bsddb3
! except ImportError:
! bsddb3 = None
try:
import bsddb
except ImportError:
! bsddb = None
! else:
! # This name is not in the old (bad) one.
! if not hasattr(bsddb, "db"):
! bsddb = None
! assert bsddb or bsddb3, \
! "Don't build binary versions without bsddb!"
# This is a little bit of a hack . We are generally in a child
***************
*** 181,186 ****
db_extension = None # for pychecker - overwritten by subclass
def __init__(self, bayes_base_name, mdb_base_name):
! self.bayes_filename = bayes_base_name + self.db_extension
! self.mdb_filename = mdb_base_name + self.db_extension
def new_bayes(self):
# Just delete the file and do an "open"
--- 183,190 ----
db_extension = None # for pychecker - overwritten by subclass
def __init__(self, bayes_base_name, mdb_base_name):
! self.bayes_filename = bayes_base_name.encode(filesystem_encoding) + \
! self.db_extension
! self.mdb_filename = mdb_base_name.encode(filesystem_encoding) + \
! self.db_extension
def new_bayes(self):
# Just delete the file and do an "open"
***************
*** 197,201 ****
bayes.close()
def open_mdb(self):
! return bayes_message.open_storage(self.mdb_filename, self.klass)
def store_mdb(self, mdb):
mdb.store()
--- 201,209 ----
bayes.close()
def open_mdb(self):
! # MessageInfo storage types may lag behind, so use pickle if the
! # matching type isn't available.
! if self.klass in bayes_message._storage_types.keys():
! return bayes_message.open_storage(self.mdb_filename, self.klass)
! return bayes_message.open_storage(self.mdb_filename, "pickle")
def store_mdb(self, mdb):
mdb.store()
***************
*** 214,222 ****
db_extension = ".db"
klass = "dbm"
- def __init__(self, bayes_base_name, mdb_base_name):
- self.bayes_filename = bayes_base_name.encode(filesystem_encoding) + \
- self.db_extension
- self.mdb_filename = mdb_base_name.encode(filesystem_encoding) + \
- self.db_extension
def new_mdb(self):
try:
--- 222,225 ----
***************
*** 228,231 ****
--- 231,238 ----
return True # True means only changed records get actually written
+ class ZODBStorageManager(DBStorageManager):
+ db_extension = ".fs"
+ klass = "zodb"
+
# Encapsulates our entire classification database
# This allows a couple of different "databases" to be open at once
***************
*** 256,262 ****
self.logger.LogDebug(0, "Bayes database initialized with "
"%d spam and %d good messages" % (bayes.nspam, bayes.nham))
! if len(message_db) != bayes.nham + bayes.nspam:
! print "*** - message database has %d messages - bayes has %d - something is screwey" % \
! (len(message_db), bayes.nham + bayes.nspam)
self.bayes = bayes
self.message_db = message_db
--- 263,272 ----
self.logger.LogDebug(0, "Bayes database initialized with "
"%d spam and %d good messages" % (bayes.nspam, bayes.nham))
! # Once, we checked that the message database was the same length
! # as the training database here. However, we now store information
! # about messages that are classified but not trained in the message
! # database, so the lengths will not be equal (unless all messages
! # are trained). That step doesn't really gain us anything, anyway,
! # since it no longer would tell us useful information, so remove it.
self.bayes = bayes
self.message_db = message_db
***************
*** 288,296 ****
start = time.clock()
bayes = self.bayes
- # Try and work out where this count sometimes goes wrong.
- if bayes.nspam + bayes.nham != len(self.message_db):
- print "WARNING: Bayes database has %d messages, " \
- "but training database has %d" % \
- (bayes.nspam + bayes.nham, len(self.message_db))
if self.logger.verbose:
--- 298,301 ----
***************
*** 328,332 ****
def GetStorageManagerClass():
! return [PickleStorageManager, DBStorageManager][use_db]
# Our main "bayes manager"
--- 333,353 ----
def GetStorageManagerClass():
! # We used to enforce this so that all binary users used bsddb, and
! # unless they modified the source, so would all source users. We
! # would like more flexibility now, so we match what the rest of the
! # applications do - this isn't exposed via the GUI, so Outlook users
! # still get bsddb by default, and have to fiddle with a text file
! # to change that.
! use_db = bayes_options["Storage", "persistent_use_database"]
! available = {"pickle" : PickleStorageManager,
! "dbm" : DBStorageManager,
! "zodb" : ZODBStorageManager,
! }
! if use_db not in available:
! # User is trying to use something fancy which isn't available.
! # Fall back on bsddb.
! print use_db, "storage type not available. Using bsddb."
! use_db = "dbm"
! return available[use_db]
# Our main "bayes manager"
From anadelonbrin at users.sourceforge.net Mon Dec 20 04:40:30 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 04:40:33 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.142,1.143
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1870/Outlook2000
Modified Files:
addin.py
Log Message:
Fix a bug that prevented the "show clues" message working with messages that hadn't
been classified.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.142
retrieving revision 1.143
diff -C2 -d -r1.142 -r1.143
*** addin.py 6 Dec 2004 18:04:34 -0000 1.142
--- addin.py 20 Dec 2004 03:40:26 -0000 1.143
***************
*** 471,482 ****
# people realise that it may not necessarily be the same, and will
# help diagnosing any 'wrong' scoring reported.
! original_score = 100 * msgstore_message.GetField(\
mgr.config.general.field_score_name)
! if original_score >= mgr.config.filter.spam_threshold:
! original_class = "spam"
! elif original_score >= mgr.config.filter.unsure_threshold:
! original_class = "unsure"
! else:
! original_class = "good"
push("
\n")
if original_score is None:
--- 471,484 ----
# people realise that it may not necessarily be the same, and will
# help diagnosing any 'wrong' scoring reported.
! original_score = msgstore_message.GetField(\
mgr.config.general.field_score_name)
! if original_score is not None:
! original_score *= 100.0
! if original_score >= mgr.config.filter.spam_threshold:
! original_class = "spam"
! elif original_score >= mgr.config.filter.unsure_threshold:
! original_class = "unsure"
! else:
! original_class = "good"
push("
\n")
if original_score is None:
From anadelonbrin at users.sourceforge.net Mon Dec 20 04:45:42 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 04:45:45 2004
Subject: [Spambayes-checkins] spambayes/spambayes ProxyUI.py,1.45,1.45.4.1
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2964/spambayes
Modified Files:
Tag: release_1_0-branch
ProxyUI.py
Log Message:
Backport fix for cgi.escaping subject lines.
Index: ProxyUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v
retrieving revision 1.45
retrieving revision 1.45.4.1
diff -C2 -d -r1.45 -r1.45.4.1
*** ProxyUI.py 16 Mar 2004 05:08:31 -0000 1.45
--- ProxyUI.py 20 Dec 2004 03:45:39 -0000 1.45.4.1
***************
*** 329,333 ****
else:
h = self.html.reviewRow.headerValue.clone()
! h.text = text
row.optionalHeadersValues += h
--- 329,333 ----
else:
h = self.html.reviewRow.headerValue.clone()
! h.text = cgi.escape(text)
row.optionalHeadersValues += h
From anadelonbrin at users.sourceforge.net Mon Dec 20 04:49:02 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 04:49:05 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs dialog_map.py,
1.39, 1.39.4.1
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3466/Outlook2000/dialogs
Modified Files:
Tag: release_1_0-branch
dialog_map.py
Log Message:
Backport fix for [ 1078923 ] Unicode support incomplete
Index: dialog_map.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dialog_map.py,v
retrieving revision 1.39
retrieving revision 1.39.4.1
diff -C2 -d -r1.39 -r1.39.4.1
*** dialog_map.py 28 Apr 2004 22:30:13 -0000 1.39
--- dialog_map.py 20 Dec 2004 03:48:59 -0000 1.39.4.1
***************
*** 236,240 ****
"""
import os
! os.startfile(window.manager.data_directory)
def ShowLog(window):
"""Opens the log file for the current SpamBayes session
--- 236,242 ----
"""
import os
! import sys
! filesystem_encoding = sys.getfilesystemencoding()
! os.startfile(window.manager.data_directory.encode(filesystem_encoding))
def ShowLog(window):
"""Opens the log file for the current SpamBayes session
From anadelonbrin at users.sourceforge.net Mon Dec 20 04:53:58 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 04:54:00 2004
Subject: [Spambayes-checkins] spambayes/spambayes Dibbler.py, 1.13.4.1,
1.13.4.2
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4374/spambayes
Modified Files:
Tag: release_1_0-branch
Dibbler.py
Log Message:
Backport fix for AUTH digest.
Index: Dibbler.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Dibbler.py,v
retrieving revision 1.13.4.1
retrieving revision 1.13.4.2
diff -C2 -d -r1.13.4.1 -r1.13.4.2
*** Dibbler.py 15 Oct 2004 06:05:33 -0000 1.13.4.1
--- Dibbler.py 20 Dec 2004 03:53:55 -0000 1.13.4.2
***************
*** 347,351 ****
# RE to extract option="value" fields from
# digest auth login field
! _login_splitter = re.compile('([a-zA-Z])+=(".*?"|.*?),?')
def __init__(self, clientSocket, server, context):
--- 347,351 ----
# RE to extract option="value" fields from
# digest auth login field
! _login_splitter = re.compile('([a-zA-Z]+)=(".*?"|.*?),?')
def __init__(self, clientSocket, server, context):
***************
*** 631,634 ****
--- 631,640 ----
unhashedDigest = ""
if options.has_key("qop"):
+ # IE 6.0 doesn't give nc back correctly?
+ if not options["nc"]:
+ options["nc"] = "00000001"
+ # Firefox 1.0 doesn't give qop back correctly?
+ if not options["qop"]:
+ options["qop"] = "auth"
unhashedDigest = "%s:%s:%s:%s:%s:%s" % \
(HA1, nonce,
From anadelonbrin at users.sourceforge.net Mon Dec 20 05:09:33 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 05:09:35 2004
Subject: [Spambayes-checkins] spambayes/spambayes message.py, 1.49.4.5,
1.49.4.6
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6978/spambayes
Modified Files:
Tag: release_1_0-branch
message.py
Log Message:
Backport fix for setPaylod with Python 2.4.
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.49.4.5
retrieving revision 1.49.4.6
diff -C2 -d -r1.49.4.5 -r1.49.4.6
*** message.py 22 Nov 2004 23:38:34 -0000 1.49.4.5
--- message.py 20 Dec 2004 04:09:28 -0000 1.49.4.6
***************
*** 243,254 ****
# imapfilter has an example of this in action
def setPayload(self, payload):
! prs = email.Parser.Parser()
! fp = StringIO.StringIO(payload)
! # this is kindof a hack, due to the fact that the parser creates a
! # new message object, and we already have the message object
! prs._parseheaders(self, fp)
! # we may want to do some header parsing error handling here
! # to try to extract important headers regardless of malformations
! prs._parsebody(self, fp)
def setId(self, id):
--- 243,252 ----
# imapfilter has an example of this in action
def setPayload(self, payload):
! # Python 2.4 has a new email package, and the private functions
! # are gone. So the old method won't work. We do this much nicer
! # with 1.1, but we have to do something to get this to work for the
! # 1.0.x branch, so use a different ugly hack.
! new_me = email.message_from_string(payload, _class=Message)
! self.__dict__ = new_me.__dict__
def setId(self, id):
***************
*** 343,348 ****
Adds routines to add/remove headers for Spambayes'''
! def __init__(self):
! Message.__init__(self)
def setIdFromPayload(self):
--- 341,347 ----
Adds routines to add/remove headers for Spambayes'''
! def setPayload(self, payload):
! new_me = email.message_from_string(payload, _class=Message)
! self.__dict__ = new_me.__dict__
def setIdFromPayload(self):
From anadelonbrin at users.sourceforge.net Mon Dec 20 05:12:09 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 05:12:13 2004
Subject: [Spambayes-checkins] spambayes CHANGELOG.txt,1.44.4.5,1.44.4.6
Message-ID:
Update of /cvsroot/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7388
Modified Files:
Tag: release_1_0-branch
CHANGELOG.txt
Log Message:
Bring up to date.
Index: CHANGELOG.txt
===================================================================
RCS file: /cvsroot/spambayes/spambayes/CHANGELOG.txt,v
retrieving revision 1.44.4.5
retrieving revision 1.44.4.6
diff -C2 -d -r1.44.4.5 -r1.44.4.6
*** CHANGELOG.txt 22 Nov 2004 23:49:32 -0000 1.44.4.5
--- CHANGELOG.txt 20 Dec 2004 04:12:05 -0000 1.44.4.6
***************
*** 1,4 ****
--- 1,11 ----
[Note that all dates are in English, not American format - i.e. day/month/year]
+ Release 1.0.2
+ =============
+ Tony Meyer 17/12/2004 message.py: Change the deprecated function to work in Python 2.4.
+ Tony Meyer 06/12/2004 Fix [ 1078923 ] Unicode support incomplete
+ Tony Meyer 06/12/2004 Fix the regex that the auth digest used, and handle the auth digest responses tha IE 6.0 and Firefox 1.0 give.
+ Tony Meyer 29/11/2004 Subject lines are not cgi.escape()d in the web interface, which might cause errors - fixed.
+
Release 1.0.1
=============
From anadelonbrin at users.sourceforge.net Mon Dec 20 05:12:27 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 05:12:30 2004
Subject: [Spambayes-checkins] spambayes CHANGELOG.txt,1.49,1.50
Message-ID:
Update of /cvsroot/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv7410
Modified Files:
CHANGELOG.txt
Log Message:
Bring up to date.
Index: CHANGELOG.txt
===================================================================
RCS file: /cvsroot/spambayes/spambayes/CHANGELOG.txt,v
retrieving revision 1.49
retrieving revision 1.50
diff -C2 -d -r1.49 -r1.50
*** CHANGELOG.txt 22 Nov 2004 23:49:58 -0000 1.49
--- CHANGELOG.txt 20 Dec 2004 04:12:25 -0000 1.50
***************
*** 3,6 ****
--- 3,38 ----
Release 1.1a1
=============
+ Tony Meyer 20/12/2004 Allow Outlook users to select their storage method in exactly the same way as other users can.
+ Tony Meyer 20/12/2004 Outlook: add a wrapper for the ZODB (FileStorage) storage class.
+ Tony Meyer 20/12/2004 Outlook: Remove some checks that no longer need to be done (that the messageinfo db has the same length as the tokens db).
+ Tony Meyer 17/12/2004 message.py: Spit out a warning if the deprecated function is used.
+ Tony Meyer 17/12/2004 message.py: Change the deprecated function to work in Python 2.4.
+ Tony Meyer 17/12/2004 sb_imapfilter: Update extract_fetch_data to handle any number of literals/message numbers.
+ Kenny Pitt 17/12/2004 Outlook: Improve e-mail address formatting for fake Exchange headers so that it supports multiple addresses, some of which may be real Internet addresses.
+ Tony Meyer 16/12/2004 sb_imapfilter: compile re's and do this only once.
+ Tony Meyer 16/12/2004 sb_imapfilter: improve handing of extract_fetch_data.
+ Tony Meyer 16/12/2004 Outlook: Improve the faked up exchange headers that we generate if there are no Internet headers.
+ Kenny Pitt 16/12/2004 Outlook: Include Message-ID in fake Exchange headers.
+ Tony Meyer 08/12/2004 Improve test_storage.py and test_message.py.
+ Tony Meyer 08/12/2004 Add [ 848365 ] Remove subject annotations from message review page
+ Tony Meyer 08/12/2004 Add [ 1036970 ] Allow Outlook plugin to move ham to a designated folder
+ Tony Meyer 07/12/2004 Outlook: More detailed statistics in SpamBayes Manager. These roughly match the updated statistics in the sb_server Web UI.
+ Tony Meyer 06/12/2004 Fix the regex that the auth digest used, and handle the auth digest responses tha IE 6.0 and Firefox 1.0 give.
+ Tony Meyer 06/12/2004 Fix [ 1078923 ] Unicode support incomplete
+ Tony Meyer 06/12/2004 Outlook: Use PR_SENDER_NAME rather than PR_DISPLAY_NAME_A for the faked-up "From" header.
+ Kenny Pitt 04/12/2004 Add notification sound support as per patch #858925.
+ Tony Meyer 01/12/2004 When doing "setup.py sdist" an MD5 checksum and the size of the created archive(s) is printed to stdout.
+ Tony Meyer 29/11/2004 sb_server: Messages that did not have the required \r?\n\r?\n separator would just pass through spambayes unproxied. Now they are, as best as possible.
+ Tony Meyer 29/11/2004 Handle a message that does not have a proper separator in insert_exception_header.
+ Tony Meyer 29/11/2004 Change sb_server to use the centralised insert_exception_header code.
+ Tony Meyer 29/11/2004 Subject lines are not cgi.escape()d in the web interface, which might cause errors - fixed.
+ Tony Meyer 26/11/2004 Outlook: Stop using the deprecated access to the bayes database and use the manager.classifier_data directly.
+ Tony Meyer 26/11/2004 Outlook: Switch to using a spambayes.message.MessageInfo database rather than an Outlook specific one.
+ Tony Meyer 26/11/2004 Outlook: Save the current folder when doing a "delete as spam", because the message may not be in the folder it was when it was filtered, or it may not have been filtered, but we do really want to recover it to wherever it was last.
+ Tony Meyer 26/11/2004 Fix [ 1071319 ] Outlook plug in for IMAP boxes
+ Tony Meyer 26/11/2004 message.py: Handle loading an Outlook messageinfo database, and add a __len__ function to the messageinfo databases.
+ Tony Meyer 26/11/2004 message.py: The messageinfo db now needs messages to have a GetDBKey function to determine the key to store the message under. For non-Outlook message classes, this is just the same as getId().
+ Tony Meyer 24/11/2004 Moved the cleanarch script to the utilities directory and added '.py' to the filename.
+ Tony Meyer 23/11/2004 Improve OE support code, mostly from [ 800671 ] Windows GUI for easy Outlook Express mailboxes training
Tony Meyer 23/11/2004 message.py: Change MessageInfoBase's methods so that recording & retrieving a message are not private methods and are more clearly named.
Tony Meyer 23/11/2004 message.py: Change so that the messageinfodb doesn't get created/opened on import, but rather through utility functions like those in spambayes.storage.
From anadelonbrin at users.sourceforge.net Mon Dec 20 05:23:35 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 05:23:39 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.95,1.96
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9230/Outlook2000
Modified Files:
msgstore.py
Log Message:
The version doesn't appear to be easily accessible in the message properties, so use
a fixed one for the faked-up headers.
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.95
retrieving revision 1.96
diff -C2 -d -r1.95 -r1.96
*** msgstore.py 17 Dec 2004 01:37:04 -0000 1.95
--- msgstore.py 20 Dec 2004 04:23:33 -0000 1.96
***************
*** 30,34 ****
MYPR_BODY_HTML_W = 0x1013001f # ditto
MYPR_MESSAGE_ID_A = 0x1035001E # more magic (message id field used for Exchange)
- MYPR_VERSION_ID = 0x7E8FFFFD # magic that I think tells us the Outlook version
CLEAR_READ_FLAG = 0x00000004
--- 30,33 ----
***************
*** 989,997 ****
prop_ids = PR_SUBJECT_A, PR_SENDER_NAME_A, PR_DISPLAY_TO_A, \
PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
! MYPR_MESSAGE_ID_A, PR_IMPORTANCE, PR_CLIENT_SUBMIT_TIME, \
! # This property gives a 'The parameter is incorrect' error, for some
! # reason, as does, 0x7E8EFFE2, which I think is the 'pretty' version
! # number. Until that's figured out, we'll have to not get this.
! # MYPR_VERSION_ID
hr, data = self.mapi_object.GetProps(prop_ids, 0)
headers = ["X-Exchange-Message: true"]
--- 988,992 ----
prop_ids = PR_SUBJECT_A, PR_SENDER_NAME_A, PR_DISPLAY_TO_A, \
PR_DISPLAY_CC_A, PR_MESSAGE_DELIVERY_TIME, \
! MYPR_MESSAGE_ID_A, PR_IMPORTANCE, PR_CLIENT_SUBMIT_TIME,
hr, data = self.mapi_object.GetProps(prop_ids, 0)
headers = ["X-Exchange-Message: true"]
***************
*** 1005,1009 ****
("Importance", 6, False, self._format_importance),
("Date", 7, False, self._format_time),
! # ("X-Mailer", 8, False, self._format_version),
):
if potentially_large:
--- 1000,1004 ----
("Importance", 6, False, self._format_importance),
("Date", 7, False, self._format_time),
! ("X-Mailer", 7, False, self._format_version),
):
if potentially_large:
***************
*** 1033,1039 ****
return {0 : "low", 1 : "normal", 2 : "high"}[raw]
! def _format_version(self, raw):
! # Data is just a version string, so prepend something to it.
! return "Exchange Client " + raw
_address_re = re.compile(r"[()<>,:@!/=; ]")
--- 1028,1033 ----
return {0 : "low", 1 : "normal", 2 : "high"}[raw]
! def _format_version(self, unused):
! return "Microsoft Exchange Client"
_address_re = re.compile(r"[()<>,:@!/=; ]")
From anadelonbrin at users.sourceforge.net Mon Dec 20 05:24:57 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 20 05:24:59 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.143,1.144
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9431/Outlook2000
Modified Files:
addin.py
Log Message:
Round the original score, don't truncate - otherwise it looks like the scores don't
match!
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.143
retrieving revision 1.144
diff -C2 -d -r1.143 -r1.144
*** addin.py 20 Dec 2004 03:40:26 -0000 1.143
--- addin.py 20 Dec 2004 04:24:54 -0000 1.144
***************
*** 485,488 ****
--- 485,489 ----
push("This message has not been filtered.")
else:
+ original_score = round(original_score)
push("When this message was last filtered, it was classified " \
"as %s (it scored %d%%)." % (original_class, original_score))
From kpitt at users.sourceforge.net Mon Dec 20 16:42:34 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Mon Dec 20 16:42:38 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_server.py,1.31,1.32
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10663
Modified Files:
sb_server.py
Log Message:
On a RETR command, my POP3 server returns the message size following the
"+OK" in the status line. That caused SpamBayes to think it was an error
because the entire status line didn't exactly match "+OK". To prevent this,
split off the first word of the status line and only compare that to "+OK".
Index: sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v
retrieving revision 1.31
retrieving revision 1.32
diff -C2 -d -r1.31 -r1.32
*** sb_server.py 29 Nov 2004 00:17:58 -0000 1.31
--- sb_server.py 20 Dec 2004 15:42:17 -0000 1.32
***************
*** 474,478 ****
# Break off the first line, which will be '+OK'.
! ok, messageText = response.split('\n', 1)
if ok.strip().upper() != "+OK":
# Must be an error response. Return unproxied.
--- 474,479 ----
# Break off the first line, which will be '+OK'.
! statusLine, messageText = response.split('\n', 1)
! ok, statusRemainder = statusLine.split(None, 1)
if ok.strip().upper() != "+OK":
# Must be an error response. Return unproxied.
From anadelonbrin at users.sourceforge.net Tue Dec 21 00:24:12 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Tue Dec 21 00:24:15 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_server.py,1.32,1.33
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24135/scripts
Modified Files:
sb_server.py
Log Message:
Kenny's fix worked if there was more than +OK, but not if there was just +OK. Fix
the fix so that both cases work.
Index: sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v
retrieving revision 1.32
retrieving revision 1.33
diff -C2 -d -r1.32 -r1.33
*** sb_server.py 20 Dec 2004 15:42:17 -0000 1.32
--- sb_server.py 20 Dec 2004 23:24:03 -0000 1.33
***************
*** 457,461 ****
"""Adds the judgement header based on the raw headers and body
of the message."""
! # Previously, we used '\n\r?\n' to detect the end of the headers in
# case of broken emails that don't use the proper line separators,
# and if we couldn't find it, then we assumed that the response was
--- 457,461 ----
"""Adds the judgement header based on the raw headers and body
of the message."""
! # Previous, we used '\n\r?\n' to detect the end of the headers in
# case of broken emails that don't use the proper line separators,
# and if we couldn't find it, then we assumed that the response was
***************
*** 475,479 ****
# Break off the first line, which will be '+OK'.
statusLine, messageText = response.split('\n', 1)
! ok, statusRemainder = statusLine.split(None, 1)
if ok.strip().upper() != "+OK":
# Must be an error response. Return unproxied.
--- 475,480 ----
# Break off the first line, which will be '+OK'.
statusLine, messageText = response.split('\n', 1)
! statusData = statusLine.split()
! ok = statusData[0]
if ok.strip().upper() != "+OK":
# Must be an error response. Return unproxied.
From anadelonbrin at users.sourceforge.net Tue Dec 21 00:26:38 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Tue Dec 21 00:26:42 2004
Subject: [Spambayes-checkins]
spambayes/spambayes/test test_sb_server.py, 1.3, 1.4
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24839/spambayes/test
Modified Files:
test_sb_server.py
Log Message:
If run with no options, run the test (like the other scripts - I always forget to
use -z). Help is still available with -h.
Modify the server a little to test that servers that return the size of messages with
the +OK in a RETR will work.
Index: test_sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_sb_server.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** test_sb_server.py 29 Nov 2004 00:11:47 -0000 1.3
--- test_sb_server.py 20 Dec 2004 23:26:33 -0000 1.4
***************
*** 3,7 ****
"""Test the POP3 proxy is working correctly.
! When using the -z command line option, carries out a test that the
POP3 proxy can be connected to, that incoming mail is classified,
that pipelining is removed from the CAPA[bility] query, and that the
--- 3,7 ----
"""Test the POP3 proxy is working correctly.
! Given no command line options, carries out a test that the
POP3 proxy can be connected to, that incoming mail is classified,
that pipelining is removed from the CAPA[bility] query, and that the
***************
*** 17,26 ****
options:
- -z : Runs a self-test and exits.
-t : Runs a fake POP3 server on port 8110 (for testing).
-h : Displays this help message.
"""
! # This module is part of the spambayes project, which is Copyright 2002
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
--- 17,25 ----
options:
-t : Runs a fake POP3 server on port 8110 (for testing).
-h : Displays this help message.
"""
! # This module is part of the spambayes project, which is Copyright 2002-5
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
***************
*** 77,80 ****
--- 76,83 ----
"""
+ # An example of a particularly nasty malformed message - where there is
+ # no body, and no separator, which would at one point slip through
+ # SpamBayes. This is an example that Tony made up.
+
malformed1 = """From: ta-meyer@ihug.co.nz
Subject: No body, and no separator"""
***************
*** 225,229 ****
headers, body = message.split('\n\n', 1)
except ValueError:
! return "+OK\r\n%s\r\n.\r\n" % message
bodyLines = body.split('\n')[:maxLines]
message = headers + '\r\n\r\n' + '\n'.join(bodyLines)
--- 228,233 ----
headers, body = message.split('\n\n', 1)
except ValueError:
! return "+OK %d octets\r\n%s\r\n.\r\n" % (len(message),
! message)
bodyLines = body.split('\n')[:maxLines]
message = headers + '\r\n\r\n' + '\n'.join(bodyLines)
***************
*** 364,373 ****
# Read the arguments.
try:
! opts, args = getopt.getopt(sys.argv[1:], 'htz')
except getopt.error, msg:
print >>sys.stderr, str(msg) + '\n\n' + __doc__
sys.exit()
! runSelfTest = False
for opt, arg in opts:
if opt == '-h':
--- 368,378 ----
# Read the arguments.
try:
! opts, args = getopt.getopt(sys.argv[1:], 'ht')
except getopt.error, msg:
print >>sys.stderr, str(msg) + '\n\n' + __doc__
sys.exit()
! state.isTest = True
! runSelfTest = True
for opt, arg in opts:
if opt == '-h':
***************
*** 377,383 ****
state.isTest = True
state.runTestServer = True
! elif opt == '-z':
! state.isTest = True
! runSelfTest = True
state.createWorkers()
--- 382,386 ----
state.isTest = True
state.runTestServer = True
! runSelfTest = False
state.createWorkers()
***************
*** 394,399 ****
asyncore.loop()
- else:
- print >>sys.stderr, __doc__
if __name__ == '__main__':
--- 397,400 ----
From kpitt at users.sourceforge.net Tue Dec 21 17:22:03 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Tue Dec 21 17:22:06 2004
Subject: [Spambayes-checkins] spambayes/spambayes Stats.py,1.9,1.10
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10875
Modified Files:
Stats.py
Log Message:
A couple of recent changes to the definition of the MessageInfoBase class
apparently didn't get propagated to the usages in the Stats class.
Index: Stats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Stats.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -C2 -d -r1.9 -r1.10
*** Stats.py 22 Nov 2004 23:34:43 -0000 1.9
--- Stats.py 21 Dec 2004 16:21:59 -0000 1.10
***************
*** 44,47 ****
--- 44,49 ----
class Stats(object):
class __empty_msg:
+ def __init__(self):
+ self.getDBKey = self.getId
def getId(self):
return self.id
***************
*** 70,74 ****
m = self.__empty_msg()
m.id = msg
! msginfoDB._getState(m)
if m.c == 's':
# Classified as spam.
--- 72,76 ----
m = self.__empty_msg()
m.id = msg
! msginfoDB.load_msg(m)
if m.c == 's':
# Classified as spam.
From anadelonbrin at users.sourceforge.net Tue Dec 21 22:30:16 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Tue Dec 21 22:30:20 2004
Subject: [Spambayes-checkins] spambayes/spambayes/test test_stats.py, NONE,
1.1
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22111/spambayes/test
Added Files:
test_stats.py
Log Message:
Unittests for the way the stats class is now going to work.
--- NEW FILE: test_stats.py ---
# Test spambayes.Stats module.
import os
import sys
import time
import unittest
import sb_test_support
sb_test_support.fix_sys_path()
from spambayes.Stats import Stats
from spambayes.Options import options
from spambayes.message import MessageInfoPickle, Message
class StatsTest(unittest.TestCase):
def setUp(self):
self.s_cut = options["Categorization", "spam_cutoff"]
self.h_cut = options["Categorization", "ham_cutoff"]
self.h_string = options["Headers", "header_ham_string"]
self.u_string = options["Headers", "header_unsure_string"]
self.s_string = options["Headers", "header_spam_string"]
self.messageinfo_db_name = "__unittest.pik"
self.messageinfo_db = MessageInfoPickle(self.messageinfo_db_name)
self.s = Stats(self.s_cut, self.h_cut, self.messageinfo_db,
self.h_string, self.u_string, self.s_string)
def tearDown(self):
if os.path.exists(self.messageinfo_db_name):
os.remove(self.messageinfo_db_name)
def test_from_date_unset(self):
self.assertEqual(None, self.s.from_date)
def test_set_date(self):
now = time.time()
self.s.ResetTotal(permanently=True)
self.assertEqual(now, self.s.from_date)
for stat in ["num_ham", "num_spam", "num_unsure",
"num_trained_spam", "num_trained_spam_fn",
"num_trained_ham", "num_trained_ham_fp",]:
self.assertEqual(self.s.totals[stat], 0)
# Check that it was stored, too.
self.messageinfo_db.close()
self.messageinfo_db = MessageInfoPickle(self.messageinfo_db_name)
self.s = Stats(self.s_cut, self.h_cut, self.messageinfo_db,
self.h_string, self.u_string, self.s_string)
self.assertEqual(now, self.s.from_date)
def test_no_messages(self):
self.assertEqual(self.s.GetStats(), ["Messages classified: 0"])
def test_reset_session(self):
self.s.RecordClassification(.2)
self.s.RecordClassification(.1)
self.s.RecordClassification(.4)
self.s.RecordClassification(.91)
self.s.RecordTraining(True, 0.1)
self.s.RecordTraining(True, 0.91)
self.s.RecordTraining(False, 0.1)
self.s.RecordTraining(False, 0.91)
self.assertNotEqual(self.s.num_ham, 0)
self.assertNotEqual(self.s.num_spam, 0)
self.assertNotEqual(self.s.num_unsure, 0)
self.assertNotEqual(self.s.num_trained_spam, 0)
self.assertNotEqual(self.s.num_trained_spam_fn, 0)
self.assertNotEqual(self.s.num_trained_ham, 0)
self.assertNotEqual(self.s.num_trained_ham_fp, 0)
self.s.Reset()
self.assertEqual(self.s.num_ham, 0)
self.assertEqual(self.s.num_spam, 0)
self.assertEqual(self.s.num_unsure, 0)
self.assertEqual(self.s.num_trained_spam, 0)
self.assertEqual(self.s.num_trained_spam_fn, 0)
self.assertEqual(self.s.num_trained_ham, 0)
self.assertEqual(self.s.num_trained_ham_fp, 0)
def test_record_ham(self):
self.s.RecordClassification(0.0)
self.assertEqual(self.s.num_ham, 1)
self.s.RecordClassification(0.0)
self.assertEqual(self.s.num_ham, 2)
def test_record_spam(self):
self.s.RecordClassification(1.0)
self.assertEqual(self.s.num_spam, 1)
self.s.RecordClassification(1.0)
self.assertEqual(self.s.num_spam, 2)
def test_record_unsure(self):
self.s.RecordClassification(0.5)
self.assertEqual(self.s.num_unsure, 1)
self.s.RecordClassification(0.5)
self.assertEqual(self.s.num_unsure, 2)
def test_record_fp(self):
self.s.RecordTraining(True, 1.0)
self.assertEqual(self.s.num_trained_ham, 1)
self.assertEqual(self.s.num_trained_ham_fp, 1)
def test_record_fn(self):
self.s.RecordTraining(False, 0.0)
self.assertEqual(self.s.num_trained_spam, 1)
self.assertEqual(self.s.num_trained_spam_fn, 1)
def test_record_train_spam(self):
self.s.RecordTraining(False, 1.0)
self.assertEqual(self.s.num_trained_spam, 1)
self.assertEqual(self.s.num_trained_spam_fn, 0)
def test_record_train_ham(self):
self.s.RecordTraining(True, 0.0)
self.assertEqual(self.s.num_trained_ham, 1)
self.assertEqual(self.s.num_trained_ham_fp, 0)
def test_calculate_persistent_stats(self):
# Make sure it is empty to start with.
for stat in ["num_ham", "num_spam", "num_unsure",
"num_trained_spam", "num_trained_spam_fn",
"num_trained_ham", "num_trained_ham_fp",]:
self.assertEqual(self.s.totals[stat], 0)
# Stuff some things in to calculate.
msg = Message('0', self.messageinfo_db)
msg.RememberTrained(True)
msg.RememberClassification(options['Headers','header_spam_string'])
msg = Message('1', self.messageinfo_db)
msg.RememberTrained(False)
msg.RememberClassification(options['Headers','header_spam_string'])
msg = Message('2', self.messageinfo_db)
msg.RememberTrained(True)
msg.RememberClassification(options['Headers','header_ham_string'])
msg = Message('3', self.messageinfo_db)
msg.RememberTrained(False)
msg.RememberClassification(options['Headers','header_ham_string'])
msg = Message('4', self.messageinfo_db)
msg.RememberClassification(options['Headers','header_ham_string'])
msg = Message('5', self.messageinfo_db)
msg.RememberTrained(False)
msg.RememberClassification(options['Headers','header_unsure_string'])
msg = Message('6', self.messageinfo_db)
msg.RememberTrained(True)
msg.RememberClassification(options['Headers','header_unsure_string'])
msg = Message('7', self.messageinfo_db)
msg.RememberClassification(options['Headers','header_unsure_string'])
msg = Message('8', self.messageinfo_db)
msg.RememberClassification(options['Headers','header_unsure_string'])
self.s.CalculatePersistentStats()
self.assertEqual(self.s.totals["num_ham"], 3)
self.assertEqual(self.s.totals["num_spam"], 2)
self.assertEqual(self.s.totals["num_unsure"], 4)
self.assertEqual(self.s.totals["num_trained_spam"], 1)
self.assertEqual(self.s.totals["num_trained_spam_fn"], 1)
self.assertEqual(self.s.totals["num_trained_ham"], 1)
self.assertEqual(self.s.totals["num_trained_ham_fp"], 1)
def test_CalculateAdditional(self):
data = {}
data["num_seen"] = 45
data["num_ham"] = 23
data["num_spam"] = 10
data["num_unsure"] = 12
data["num_trained_spam_fn"] = 4
data["num_trained_ham_fp"] = 3
data["num_trained_ham"] = 7
data["num_trained_spam"] = 5
data["num_unsure_trained_ham"] = 2
data["num_unsure_trained_spam"] = 1
new_data = self.s._CalculateAdditional(data)
self.assertEqual(new_data["perc_ham"], 100.0 * data["num_ham"] /
data["num_seen"])
self.assertEqual(new_data["perc_spam"], 100.0 * data["num_spam"] /
data["num_seen"])
self.assertEqual(new_data["perc_unsure"], 100.0 *
data["num_unsure"] / data["num_seen"])
self.assertEqual(new_data["num_ham_correct"], data["num_ham"] -
data["num_trained_spam_fn"])
self.assertEqual(new_data["num_spam_correct"], data["num_spam"] -
data["num_trained_ham_fp"])
self.assertEqual(new_data["num_correct"],
new_data["num_ham_correct"] +
new_data["num_spam_correct"])
self.assertEqual(new_data["num_incorrect"],
data["num_trained_spam_fn"] +
data["num_trained_ham_fp"])
self.assertEqual(new_data["perc_correct"], 100.0 *
new_data["num_correct"] / data["num_seen"])
self.assertEqual(new_data["perc_incorrect"], 100.0 *
new_data["num_incorrect"] / data["num_seen"])
self.assertEqual(new_data["perc_fp"], 100.0 *
data["num_trained_ham_fp"] / data["num_seen"])
self.assertEqual(new_data["perc_fn"], 100.0 *
data["num_trained_spam_fn"] / data["num_seen"])
self.assertEqual(new_data["num_unsure_trained_ham"],
data["num_trained_ham"] -
data["num_trained_ham_fp"])
self.assertEqual(new_data["num_unsure_trained_spam"],
data["num_trained_spam"] -
data["num_trained_spam_fn"])
self.assertEqual(new_data["num_unsure_not_trained"],
data["num_unsure"] -
data["num_unsure_trained_ham"] -
data["num_unsure_trained_spam"])
self.assertEqual(new_data["perc_unsure_trained_ham"], 100.0 *
data["num_unsure_trained_ham"] /
data["num_unsure"])
self.assertEqual(new_data["perc_unsure_trained_spam"], 100.0 *
data["num_unsure_trained_spam"] /
data["num_unsure"])
self.assertEqual(new_data["perc_unsure_not_trained"], 100.0 *
new_data["num_unsure_not_trained"] /
data["num_unsure"])
self.assertEqual(new_data["total_ham"],
new_data["num_ham_correct"] +
data["num_trained_ham"])
self.assertEqual(new_data["total_spam"],
new_data["num_spam_correct"] +
data["num_trained_spam"])
self.assertEqual(new_data["perc_ham_incorrect"], 100.0 *
data["num_trained_ham_fp"] /
data["total_ham"])
self.assertEqual(new_data["perc_ham_unsure"], 100.0 *
data["num_unsure_trained_ham"] /
data["total_ham"])
self.assertEqual(new_data["perc_ham_incorrect_or_unsure"], 100.0 *
(data["num_trained_ham_fp"] +
data["num_unsure_trained_ham"]) /
data["total_ham"])
self.assertEqual(new_data["perc_spam_correct"], 100.0 *
data["num_spam_correct"] /
data["total_spam"])
self.assertEqual(new_data["perc_spam_unsure"], 100.0 *
data["num_unsure_trained_spam"] /
data["total_spam"])
self.assertEqual(new_data["perc_spam_correct_or_unsure"], 100.0 *
(data["num_spam_correct"] +
data["num_unsure_trained_spam"]) /
data["total_spam"])
def test_AddPercentStrings(self):
for i in xrange(10):
self._test_AddPercentStrings(i)
def _test_AddPercentStrings(self, dp):
data = self.s._AddPercentStrings({}, dp)
self.assertEqual(data["perc_ham_s"],
"%%(perc_ham).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_spam_s"],
"%%(perc_spam).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_unsure_s"],
"%%(perc_unsure).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_correct_s"],
"%%(perc_correct).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_incorrect_s"],
"%%(perc_incorrect).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_fp_s"],
"%%(perc_fp).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_fn_s"],
"%%(perc_fn).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_spam_correct_s"],
"%%(perc_spam_correct).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_spam_unsure_s"],
"%%(perc_spam_unsure).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_spam_correct_or_unsure_s"],
"%%(perc_spam_correct_or_unsure).%df%%(perc)s" %
(dp,))
self.assertEqual(data["perc_ham_incorrect_s"],
"%%(perc_ham_incorrect).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_ham_unsure_s"],
"%%(perc_ham_unsure).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_ham_incorrect_or_unsure_s"],
"%%(perc_ham_incorrect_or_unsure).%df%%(perc)s" %
(dp,))
self.assertEqual(data["perc_unsure_trained_ham_s"],
"%%(perc_unsure_trained_ham).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc_unsure_trained_spam_s"],
"%%(perc_unsure_trained_spam).%df%%(perc)s" %
(dp,))
self.assertEqual(data["perc_unsure_not_trained_s"],
"%%(perc_unsure_not_trained).%df%%(perc)s" % (dp,))
self.assertEqual(data["perc"], "%")
def _test_no_recovery(self, score, one, two, three, five):
self.s.RecordClassification(score)
s = self.s.GetStats()
self.assertEqual(s[0], "Messages classified: 1")
self.assertEqual(s[1], one)
self.assertEqual(s[2], two)
self.assertEqual(s[3], three)
self.assertEqual(s[4], "")
self.assertEqual(s[5], five)
self.assertEqual(s[6], "Classified incorrectly:\t0 (0.0% of total)")
self.assertEqual(s[7], "")
self.assertEqual(s[8], "Manually classified as good:\t0")
self.assertEqual(s[9], "Manually classified as spam:\t0")
self.assertEqual(s[10], "")
if self.h_cut <= score < self.s_cut:
self.assertEqual(s[11], "Unsures trained as good:\t0 (0.0% of unsures)")
self.assertEqual(s[12], "Unsures trained as spam:\t0 (0.0% of unsures)")
self.assertEqual(s[13], "Unsures not trained:\t\t1 (100.0% of unsures)")
self.assertEqual(s[14], "")
counter = 15
else:
counter = 11
try:
return s[counter]
except IndexError:
return
def test_no_recovery_unsure(self):
score = 0.2
one = "\tGood:\t0 (0.0%)"
two = "\tSpam:\t0 (0.0%)"
three = "\tUnsure:\t1 (100.0%)"
five = "Classified correctly:\t0 (0.0% of total)"
self._test_no_recovery(score, one, two, three, five)
def test_no_recovery_ham(self):
score = 0.0
one = "\tGood:\t1 (100.0%)"
two = "\tSpam:\t0 (0.0%)"
three = "\tUnsure:\t0 (0.0%)"
five = "Classified correctly:\t1 (100.0% of total)"
final = self._test_no_recovery(score, one, two, three, five)
self.assertEqual(final, "Good incorrectly identified:\t0.0% (+ 0.0% unsure)")
def test_no_recovery_spam(self):
score = 1.0
one = "\tGood:\t0 (0.0%)"
two = "\tSpam:\t1 (100.0%)"
three = "\tUnsure:\t0 (0.0%)"
five = "Classified correctly:\t1 (100.0% of total)"
final = self._test_no_recovery(score, one, two, three, five)
self.assertEqual(final, "Spam correctly identified:\t100.0% (+ 0.0% unsure)")
def test_get_stats_session_only(self):
# Record some session data.
self.s.RecordClassification(0.0)
self.s.RecordClassification(0.2)
self.s.RecordClassification(0.1)
self.s.RecordClassification(0.4)
self.s.RecordClassification(0.5)
self.s.RecordClassification(0.95)
self.s.RecordClassification(1.0)
self.s.RecordTraining(True, 0.1)
self.s.RecordTraining(True, 1.0)
self.s.RecordTraining(False, 0.1)
self.s.RecordTraining(False, 1.0)
# Put data into the totals, to ensure that we only get back the
# session data.
for stat in ["num_ham", "num_spam", "num_unsure",
"num_trained_spam", "num_trained_spam_fn",
"num_trained_ham", "num_trained_ham_fp",]:
self.s.totals[stat] = 4
s = self.s.GetStats(session_only=True)
self.assertEqual(s[0], "Messages classified: 7")
self.assertEqual(s[1], "\tGood:\t2 (28.6%)")
self.assertEqual(s[2], "\tSpam:\t2 (28.6%)")
self.assertEqual(s[3], "\tUnsure:\t3 (42.9%)")
self.assertEqual(s[4], "")
self.assertEqual(s[5], "Classified correctly:\t2 (28.6% of total)")
self.assertEqual(s[6], "Classified incorrectly:\t2 (28.6% of total)")
self.assertEqual(s[7], "\tFalse positives:\t1 (14.3% of total)")
self.assertEqual(s[8], "\tFalse negatives:\t1 (14.3% of total)")
self.assertEqual(s[9], "")
self.assertEqual(s[10], "Manually classified as good:\t2")
self.assertEqual(s[11], "Manually classified as spam:\t2")
self.assertEqual(s[12], "")
self.assertEqual(s[13], "Unsures trained as good:\t1 (33.3% of unsures)")
self.assertEqual(s[14], "Unsures trained as spam:\t1 (33.3% of unsures)")
self.assertEqual(s[15], "Unsures not trained:\t\t1 (33.3% of unsures)")
self.assertEqual(s[16], "")
self.assertEqual(s[17], "Spam correctly identified:\t33.3% (+ 33.3% unsure)")
self.assertEqual(s[18], "Good incorrectly identified:\t33.3% (+ 33.3% unsure)")
self.assertEqual(len(s), 19)
def test_get_all_stats(self):
s = self._stuff_with_data()
self.assertEqual(s[0], "Messages classified: 16")
self.assertEqual(s[1], "\tGood:\t5 (31.3%)")
self.assertEqual(s[2], "\tSpam:\t4 (25.0%)")
self.assertEqual(s[3], "\tUnsure:\t7 (43.8%)")
self.assertEqual(s[4], "")
self.assertEqual(s[5], "Classified correctly:\t5 (31.3% of total)")
self.assertEqual(s[6], "Classified incorrectly:\t4 (25.0% of total)")
self.assertEqual(s[7], "\tFalse positives:\t2 (12.5% of total)")
self.assertEqual(s[8], "\tFalse negatives:\t2 (12.5% of total)")
self.assertEqual(s[9], "")
self.assertEqual(s[10], "Manually classified as good:\t3")
self.assertEqual(s[11], "Manually classified as spam:\t3")
self.assertEqual(s[12], "")
self.assertEqual(s[13], "Unsures trained as good:\t1 (14.3% of unsures)")
self.assertEqual(s[14], "Unsures trained as spam:\t1 (14.3% of unsures)")
self.assertEqual(s[15], "Unsures not trained:\t\t5 (71.4% of unsures)")
self.assertEqual(s[16], "")
self.assertEqual(s[17], "Spam correctly identified:\t40.0% (+ 20.0% unsure)")
self.assertEqual(s[18], "Good incorrectly identified:\t33.3% (+ 16.7% unsure)")
self.assertEqual(len(s), 19)
def _stuff_with_data(self, use_html=False):
# Record some session data.
self.s.RecordClassification(0.0)
self.s.RecordClassification(0.2)
self.s.RecordClassification(0.1)
self.s.RecordClassification(0.4)
self.s.RecordClassification(0.5)
self.s.RecordClassification(0.95)
self.s.RecordClassification(1.0)
self.s.RecordTraining(True, 0.1)
self.s.RecordTraining(True, 1.0)
self.s.RecordTraining(False, 0.1)
self.s.RecordTraining(False, 1.0)
# Put data into the totals.
msg = Message('0', self.messageinfo_db)
msg.RememberTrained(True)
msg.RememberClassification(options['Headers','header_spam_string'])
msg = Message('1', self.messageinfo_db)
msg.RememberTrained(False)
msg.RememberClassification(options['Headers','header_spam_string'])
msg = Message('2', self.messageinfo_db)
msg.RememberTrained(True)
msg.RememberClassification(options['Headers','header_ham_string'])
msg = Message('3', self.messageinfo_db)
msg.RememberTrained(False)
msg.RememberClassification(options['Headers','header_ham_string'])
msg = Message('4', self.messageinfo_db)
msg.RememberClassification(options['Headers','header_ham_string'])
msg = Message('5', self.messageinfo_db)
msg.RememberTrained(False)
msg.RememberClassification(options['Headers','header_unsure_string'])
msg = Message('6', self.messageinfo_db)
msg.RememberTrained(True)
msg.RememberClassification(options['Headers','header_unsure_string'])
msg = Message('7', self.messageinfo_db)
msg.RememberClassification(options['Headers','header_unsure_string'])
msg = Message('8', self.messageinfo_db)
msg.RememberClassification(options['Headers','header_unsure_string'])
self.s.CalculatePersistentStats()
return self.s.GetStats(use_html=use_html)
def test_with_html(self):
s = self._stuff_with_data(True)
for line in s:
self.assert_('\t' not in line)
def test_without_html(self):
s = self._stuff_with_data(False)
for line in s:
self.assert_(' ' not in line)
def suite():
suite = unittest.TestSuite()
for cls in (StatsTest,
):
suite.addTest(unittest.makeSuite(cls))
return suite
if __name__=='__main__':
sb_test_support.unittest_main(argv=sys.argv + ['suite'])
From anadelonbrin at users.sourceforge.net Tue Dec 21 22:37:10 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Tue Dec 21 22:37:14 2004
Subject: [Spambayes-checkins] spambayes/spambayes UserInterface.py, 1.48,
1.49 message.py, 1.62, 1.63
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv23974/spambayes
Modified Files:
UserInterface.py message.py
Log Message:
Store a stats start date in the message database, so that we can 'reset' the statistics
without removing the whole message database. If the database grows too large now,
we can also age out data from it if we want to.
Allow message.Messages to have their id specified on creation. Also allow specification
of the messageinfo_db, which means one object can be shared rather than each message
creating it's own one. Only the test suite uses this at the moment, but other code
should, too.
Update a few comments in UserInterface.py and use keyword args when calling the Stats.
Index: UserInterface.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v
retrieving revision 1.48
retrieving revision 1.49
diff -C2 -d -r1.48 -r1.49
*** UserInterface.py 10 Aug 2004 14:20:24 -0000 1.48
--- UserInterface.py 21 Dec 2004 21:37:06 -0000 1.49
***************
*** 503,506 ****
--- 503,512 ----
# the database later. This is a temporary implementation -
# it should keep a Corpus of trained messages.
+ # XXX Temporary, heh. One of the problems with this is that
+ # XXX these files get opened in whatever happens to be the cwd.
+ # XXX I don't think anyone uses these anyway, but we should fix
+ # XXX this for 1.1. I think that creating a new message in the
+ # XXX Ham/Spam corpus would work, and not interfere with anything.
+ # XXX We could later search for them, too, which would be a bonus.
if isSpam:
f = open("_pop3proxyspam.mbox", "a")
***************
*** 512,515 ****
--- 518,524 ----
self.flush()
for message in messages:
+ # XXX Here, we should really use the message.Message class,
+ # XXX so that the messageinfo database is updated (and so
+ # XXX the stats are correct, and so on).
tokens = tokenizer.tokenize(message)
self.classifier.learn(tokens, isSpam)
***************
*** 913,919 ****
# rather than regenerating it every time. If people complain
# about it being too slow, then do this!
s = Stats.Stats()
self._writePreamble("Statistics")
! stats = s.GetStats()
stats = self._buildBox("Statistics", None, "
".join(stats))
self.write(stats)
--- 922,930 ----
# rather than regenerating it every time. If people complain
# about it being too slow, then do this!
+ # XXX The Stats object should be generated once, when we start up,
+ # XXX and then just called, here.
s = Stats.Stats()
self._writePreamble("Statistics")
! stats = s.GetStats(use_html=True)
stats = self._buildBox("Statistics", None, "
".join(stats))
self.write(stats)
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.62
retrieving revision 1.63
diff -C2 -d -r1.62 -r1.63
*** message.py 17 Dec 2004 01:23:36 -0000 1.62
--- message.py 21 Dec 2004 21:37:06 -0000 1.63
***************
*** 83,86 ****
--- 83,87 ----
import sys
import types
+ import time
import math
import re
***************
*** 111,114 ****
--- 112,117 ----
CRLF_RE = re.compile(r'\r\n|\r|\n')
+ STATS_START_KEY = "Statistics start date"
+
class MessageInfoBase(object):
def __init__(self, db_name):
***************
*** 118,121 ****
--- 121,134 ----
return len(self.db)
+ def get_statistics_start_date(self):
+ if STATS_START_KEY in self.db:
+ return self.db[STATS_START_KEY]
+ else:
+ return None
+
+ def set_statistics_start_date(self, date):
+ self.db[STATS_START_KEY] = date
+ self.store()
+
def load_msg(self, msg):
if self.db is not None:
***************
*** 150,154 ****
return
else:
! print >> sys.stderr, "Unknown message info type"
sys.exit(1)
for att, val in attributes:
--- 163,168 ----
return
else:
! print >> sys.stderr, "Unknown message info type", \
! attributes
sys.exit(1)
for att, val in attributes:
***************
*** 157,161 ****
def store_msg(self, msg):
if self.db is not None:
! attributes = []
for att in msg.stored_attributes:
attributes.append((att, getattr(msg, att)))
--- 171,175 ----
def store_msg(self, msg):
if self.db is not None:
! attributes = [("date_modified", time.time())]
for att in msg.stored_attributes:
attributes.append((att, getattr(msg, att)))
***************
*** 264,273 ****
'''An email.Message.Message extended for SpamBayes'''
! def __init__(self):
email.Message.Message.__init__(self)
# persistent state
! nm, typ = database_type()
! self.message_info_db = open_storage(nm, typ)
self.stored_attributes = ['c', 't',]
self.getDBKey = self.getId
--- 278,291 ----
'''An email.Message.Message extended for SpamBayes'''
! def __init__(self, id=None, message_info_db=None):
email.Message.Message.__init__(self)
# persistent state
! # (non-persistent state includes all of email.Message.Message state)
! if message_info_db is not None:
! self.message_info_db = message_info_db
! else:
! nm, typ = database_type()
! self.message_info_db = open_storage(nm, typ)
self.stored_attributes = ['c', 't',]
self.getDBKey = self.getId
***************
*** 276,280 ****
self.t = None
! # non-persistent state includes all of email.Message.Message state
# This function (and it's hackishness) can be avoided by using
--- 294,299 ----
self.t = None
! if id is not None:
! self.setId(id)
# This function (and it's hackishness) can be avoided by using
***************
*** 315,318 ****
--- 334,340 ----
raise TypeError, "Id must be a string"
+ if id == STATS_START_KEY:
+ raise ValueError, "MsgId must not be" + STATS_START_KEY
+
self.id = id
self.message_info_db.load_msg(self)
From anadelonbrin at users.sourceforge.net Tue Dec 21 22:41:52 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Tue Dec 21 22:41:55 2004
Subject: [Spambayes-checkins] spambayes/spambayes Stats.py,1.10,1.11
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv25155/spambayes
Modified Files:
Stats.py
Log Message:
Merge the Outlook2000.oastats.Stats class and the spambayes.Stats.Stats class into
one that all the applications can use. Hopefully this combines the best features
of each, and allows us to make improvements to both much more easily.
The stats are sourced from the messageinfo db (no more need for the pickle the Outlook
plugin was using - this was only used by CVS users, so they can delete it manually).
Resetting is done by only counting messages since a certain date, which can be changed
to the current date/time.
Per-session and persistent (total) stats are supported, in much the same way as the
Outlook stats have always worked. The other applications can use these too, now,
although they don't as yet. The code is moderately backwards compatible.
The code that generates the stats strings is broken up into three functions now, to
make it easier to test it (and, hopefully, modify it).
Index: Stats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Stats.py,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** Stats.py 21 Dec 2004 16:21:59 -0000 1.10
--- Stats.py 21 Dec 2004 21:41:49 -0000 1.11
***************
*** 1,5 ****
#! /usr/bin/env python
! """Stats.py - Spambayes statistics class.
Classes:
--- 1,5 ----
#! /usr/bin/env python
! """Stats.py - SpamBayes statistics class.
Classes:
***************
*** 14,29 ****
is .
To Do:
o People would like pretty graphs, so maybe that could be done.
o People have requested time-based statistics - mail per hour,
spam per hour, and so on.
! o The possible stats to show are pretty much endless. Some to
! consider would be: percentage of mail that is fp/fn/unsure,
! percentage of mail correctly classified.
o Suggestions?
-
"""
! # This module is part of the spambayes project, which is Copyright 2002-4
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
--- 14,31 ----
is .
+ This class provides information for both the web interface, the
+ Outlook plug-in, and sb_pop3dnd.
+
To Do:
o People would like pretty graphs, so maybe that could be done.
o People have requested time-based statistics - mail per hour,
spam per hour, and so on.
! Discussion on spambayes-dev indicated that this would be a lot
! of work for not much gain; however, since we now have some
! time data stored, it wouldn't be too bad, so maybe it can go in.
o Suggestions?
"""
! # This module is part of the spambayes project, which is Copyright 2002-5
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
***************
*** 38,246 ****
True, False = 1, 0
import types
! from spambayes.message import database_type, open_storage
! class Stats(object):
! class __empty_msg:
! def __init__(self):
! self.getDBKey = self.getId
! def getId(self):
! return self.id
! def __init__(self):
! self.CalculateStats()
def Reset(self):
! self.cls_spam = 0
! self.cls_ham = 0
! self.cls_unsure = 0
! self.trn_spam = 0
! self.trn_ham = 0
! self.trn_unsure_ham = 0
! self.trn_unsure_spam = 0
! self.fp = 0
! self.fn = 0
! self.total = 0
! def CalculateStats(self):
! self.Reset()
! nm, typ = database_type()
! msginfoDB = open_storage(nm, typ)
! for msg in msginfoDB.db.keys():
! self.total += 1
! m = self.__empty_msg()
! m.id = msg
! msginfoDB.load_msg(m)
! if m.c == 's':
# Classified as spam.
! self.cls_spam += 1
! if m.t == False:
# False positive (classified as spam, trained as ham)
! self.fp += 1
! elif m.c == 'h':
# Classified as ham.
! self.cls_ham += 1
! if m.t == True:
# False negative (classified as ham, trained as spam)
! self.fn += 1
! elif m.c == 'u':
# Classified as unsure.
! self.cls_unsure += 1
! if m.t == False:
! self.trn_unsure_ham += 1
! elif m.t == True:
! self.trn_unsure_spam += 1
! if m.t == True:
! self.trn_spam += 1
! elif m.t == False:
! self.trn_ham += 1
! def GetStats(self, use_html=True):
! if self.total == 0:
! return ["SpamBayes has processed zero messages"]
chunks = []
push = chunks.append
! not_trn_unsure = self.cls_unsure - self.trn_unsure_ham - \
! self.trn_unsure_spam
! if self.cls_unsure:
! unsure_ham_perc = 100.0 * self.trn_unsure_ham / self.cls_unsure
! unsure_spam_perc = 100.0 * self.trn_unsure_spam / self.cls_unsure
! unsure_not_perc = 100.0 * not_trn_unsure / self.cls_unsure
! else:
! unsure_ham_perc = 0.0 # Not correct, really!
! unsure_spam_perc = 0.0 # Not correct, really!
! unsure_not_perc = 0.0 # Not correct, really!
! if self.trn_ham:
! trn_perc_unsure_ham = 100.0 * self.trn_unsure_ham / \
! self.trn_ham
! trn_perc_fp = 100.0 * self.fp / self.trn_ham
! trn_perc_ham = 100.0 - (trn_perc_unsure_ham + trn_perc_fp)
! else:
! trn_perc_ham = 0.0 # Not correct, really!
! trn_perc_unsure_ham = 0.0 # Not correct, really!
! trn_perc_fp = 0.0 # Not correct, really!
! if self.trn_spam:
! trn_perc_unsure_spam = 100.0 * self.trn_unsure_spam / \
! self.trn_spam
! trn_perc_fn = 100.0 * self.fn / self.trn_spam
! trn_perc_spam = 100.0 - (trn_perc_unsure_spam + trn_perc_fn)
else:
! trn_perc_spam = 0.0 # Not correct, really!
! trn_perc_unsure_spam = 0.0 # Not correct, really!
! trn_perc_fn = 0.0 # Not correct, really!
! format_dict = {
! 'num_seen' : self.total,
! 'correct' : self.total - (self.cls_unsure + self.fp + self.fn),
! 'incorrect' : self.cls_unsure + self.fp + self.fn,
! 'unsure_ham_perc' : unsure_ham_perc,
! 'unsure_spam_perc' : unsure_spam_perc,
! 'unsure_not_perc' : unsure_not_perc,
! 'not_trn_unsure' : not_trn_unsure,
! 'trn_total' : (self.trn_ham + self.trn_spam + \
! self.trn_unsure_ham + self.trn_unsure_spam),
! 'trn_perc_ham' : trn_perc_ham,
! 'trn_perc_unsure_ham' : trn_perc_unsure_ham,
! 'trn_perc_fp' : trn_perc_fp,
! 'trn_perc_spam' : trn_perc_spam,
! 'trn_perc_unsure_spam' : trn_perc_unsure_spam,
! 'trn_perc_fn' : trn_perc_fn,
! }
! format_dict.update(self.__dict__)
! # Add percentages of everything.
! for key, val in format_dict.items():
! perc_key = "perc_" + key
! if self.total and isinstance(val, types.IntType):
! format_dict[perc_key] = 100.0 * val / self.total
! else:
! format_dict[perc_key] = 0.0 # Not correct, really!
! # Figure out plurals
! for num, key in [("num_seen", "sp1"),
! ("correct", "sp2"),
! ("incorrect", "sp3"),
! ("fp", "sp4"),
! ("fn", "sp5"),
! ("trn_unsure_ham", "sp6"),
! ("trn_unsure_spam", "sp7"),
! ("not_trn_unsure", "sp8"),
! ("trn_total", "sp9"),
! ]:
! if format_dict[num] == 1:
! format_dict[key] = ''
! else:
! format_dict[key] = 's'
! for num, key in [("correct", "wp1"),
! ("incorrect", "wp2"),
! ("not_trn_unsure", "wp3"),
! ]:
! if format_dict[num] == 1:
! format_dict[key] = 'was'
! else:
! format_dict[key] = 'were'
! # Possibly use HTML for breaks/tabs.
if use_html:
- format_dict["br"] = "
"
format_dict["tab"] = " "
else:
- format_dict["br"] = "\r\n"
format_dict["tab"] = "\t"
! ## Our result should look something like this:
! ## (devised by Mark Moraes and Kenny Pitt)
! ##
! ## SpamBayes has classified a total of 1223 messages:
! ## 827 ham (67.6% of total)
! ## 333 spam (27.2% of total)
! ## 63 unsure (5.2% of total)
! ##
! ## 1125 messages were classified correctly (92.0% of total)
! ## 35 messages were classified incorrectly (2.9% of total)
! ## 0 false positives (0.0% of total)
! ## 35 false negatives (2.9% of total)
! ##
! ## 6 unsures trained as ham (9.5% of unsures)
! ## 56 unsures trained as spam (88.9% of unsures)
! ## 1 unsure was not trained (1.6% of unsures)
! ##
! ## A total of 760 messages have been trained:
! ## 346 ham (98.3% ham, 1.7% unsure, 0.0% false positives)
! ## 414 spam (78.0% spam, 13.5% unsure, 8.5% false negatives)
- push("SpamBayes has classified a total of " \
- "%(num_seen)d message%(sp1)s:" \
- "%(br)s%(tab)s%(cls_ham)d " \
- "(%(perc_cls_ham).0f%% of total) good" \
- "%(br)s%(tab)s%(cls_spam)d " \
- "(%(perc_cls_spam).0f%% of total) spam" \
- "%(br)s%(tab)s%(cls_unsure)d " \
- "(%(perc_cls_unsure).0f%% of total) unsure." % \
- format_dict)
- push("%(correct)d message%(sp2)s %(wp1)s classified correctly " \
- "(%(perc_correct).0f%% of total)" \
- "%(br)s%(incorrect)d message%(sp3)s %(wp2)s classified " \
- "incorrectly " \
- "(%(perc_incorrect).0f%% of total)" \
- "%(br)s%(tab)s%(fp)d false positive%(sp4)s " \
- "(%(perc_fp).0f%% of total)" \
- "%(br)s%(tab)s%(fn)d false negative%(sp5)s " \
- "(%(perc_fn).0f%% of total)" % \
- format_dict)
- push("%(trn_unsure_ham)d unsure%(sp6)s trained as good " \
- "(%(unsure_ham_perc).0f%% of unsures)" \
- "%(br)s%(trn_unsure_spam)d unsure%(sp7)s trained as spam " \
- "(%(unsure_spam_perc).0f%% of unsures)" \
- "%(br)s%(not_trn_unsure)d unsure%(sp8)s %(wp3)s not trained " \
- "(%(unsure_not_perc).0f%% of unsures)" % \
- format_dict)
- push("A total of %(trn_total)d message%(sp9)s have been trained:" \
- "%(br)s%(tab)s%(trn_ham)d good " \
- "(%(trn_perc_ham)0.f%% good, %(trn_perc_unsure_ham)0.f%% " \
- "unsure, %(trn_perc_fp).0f%% false positives)" \
- "%(br)s%(tab)s%(trn_spam)d spam " \
- "(%(trn_perc_spam)0.f%% spam, %(trn_perc_unsure_spam)0.f%% " \
- "unsure, %(trn_perc_fn).0f%% false negatives)" % \
- format_dict)
return chunks
--- 40,349 ----
True, False = 1, 0
+ import time
import types
! from spambayes.message import STATS_START_KEY
! from spambayes.message import database_type, open_storage, Message
! try:
! _
! except NameError:
! _ = lambda arg: arg
! class Stats(object):
! def __init__(self, spam_threshold, unsure_threshold, messageinfo_db,
! ham_string, unsure_string, spam_string):
! self.messageinfo_db = messageinfo_db
! self.spam_threshold = spam_threshold
! self.unsure_threshold = unsure_threshold
! self.ham_string = ham_string
! self.unsure_string = unsure_string
! self.spam_string = spam_string
! # Reset session stats.
! self.Reset()
! # Load persistent stats.
! self.from_date = self.messageinfo_db.get_statistics_start_date()
! self.CalculatePersistentStats()
def Reset(self):
! self.num_ham = self.num_spam = self.num_unsure = 0
! self.num_trained_spam = self.num_trained_spam_fn = 0
! self.num_trained_ham = self.num_trained_ham_fp = 0
! def ResetTotal(self, permanently=False):
! self.totals = {}
! for stat in ["num_ham", "num_spam", "num_unsure",
! "num_trained_spam", "num_trained_spam_fn",
! "num_trained_ham", "num_trained_ham_fp",]:
! self.totals[stat] = 0
! if permanently:
! # Reset the date.
! self.from_date = time.time()
! self.messageinfo_db.set_statistics_start_date(self.from_date)
!
! def RecordClassification(self, score):
! if score >= self.spam_threshold:
! self.num_spam += 1
! elif score >= self.unsure_threshold:
! self.num_unsure += 1
! else:
! self.num_ham += 1
!
! def RecordTraining(self, as_ham, old_score):
! if as_ham:
! self.num_trained_ham += 1
! # If we are recovering an item that is in the "spam" threshold,
! # then record it as a "false positive"
! if old_score > self.spam_threshold:
! self.num_trained_ham_fp += 1
! else:
! self.num_trained_spam += 1
! # If we are deleting as Spam an item that was in our "good"
! # range, then record it as a false negative.
! if old_score < self.unsure_threshold:
! self.num_trained_spam_fn += 1
!
! def CalculatePersistentStats(self):
! """Calculate the statistics totals (i.e. not this session).
!
! This is done by running through the messageinfo database and
! adding up the various information. This could get quite time
! consuming if the messageinfo database gets very large, so
! some consideration should perhaps be made about what to do
! then.
! """
! self.ResetTotal()
! totals = self.totals
! for msg_id in self.messageinfo_db.db.keys():
! # Skip the date key.
! if msg_id == STATS_START_KEY:
! continue
! m = Message(msg_id, self.messageinfo_db)
! self.messageinfo_db.load_msg(m)
!
! # Skip ones that are too old.
! if self.from_date and m.date_modified and \
! m.date_modified > self.from_date:
! continue
!
! classification = m.GetClassification()
! trained = m.GetTrained()
!
! if classification == self.spam_string:
# Classified as spam.
! totals["num_spam"] += 1
! if trained == False:
# False positive (classified as spam, trained as ham)
! totals["num_trained_ham_fp"] += 1
! elif classification == self.ham_string:
# Classified as ham.
! totals["num_ham"] += 1
! if trained == True:
# False negative (classified as ham, trained as spam)
! totals["num_trained_spam_fn"] += 1
! elif classification == self.unsure_string:
# Classified as unsure.
! totals["num_unsure"] += 1
! if trained == False:
! totals["num_trained_ham"] += 1
! elif trained == True:
! totals["num_trained_spam"] += 1
! def _CombineSessionAndTotal(self):
! totals = self.totals
! num_seen = self.num_ham + self.num_spam + self.num_unsure + \
! totals["num_ham"] + totals["num_spam"] + \
! totals["num_unsure"]
! num_ham = self.num_ham + totals["num_ham"]
! num_spam = self.num_spam + totals["num_spam"]
! num_unsure = self.num_unsure + totals["num_unsure"]
! num_trained_ham = self.num_trained_ham + totals["num_trained_ham"]
! num_trained_ham_fp = self.num_trained_ham_fp + \
! totals["num_trained_ham_fp"]
! num_trained_spam = self.num_trained_spam + \
! totals["num_trained_spam"]
! num_trained_spam_fn = self.num_trained_spam_fn + \
! totals["num_trained_spam_fn"]
! return locals()
!
! def _CalculateAdditional(self, data):
! data["perc_ham"] = 100.0 * data["num_ham"] / data["num_seen"]
! data["perc_spam"] = 100.0 * data["num_spam"] / data["num_seen"]
! data["perc_unsure"] = 100.0 * data["num_unsure"] / data["num_seen"]
! data["num_ham_correct"] = data["num_ham"] - \
! data["num_trained_spam_fn"]
! data["num_spam_correct"] = data["num_spam"] - \
! data["num_trained_ham_fp"]
! data["num_correct"] = data["num_ham_correct"] + \
! data["num_spam_correct"]
! data["num_incorrect"] = data["num_trained_spam_fn"] + \
! data["num_trained_ham_fp"]
! data["perc_correct"] = 100.0 * data["num_correct"] / \
! data["num_seen"]
! data["perc_incorrect"] = 100.0 * data["num_incorrect"] / \
! data["num_seen"]
! data["perc_fp"] = 100.0 * data["num_trained_ham_fp"] / \
! data["num_seen"]
! data["perc_fn"] = 100.0 * data["num_trained_spam_fn"] / \
! data["num_seen"]
! data["num_unsure_trained_ham"] = data["num_trained_ham"] - \
! data["num_trained_ham_fp"]
! data["num_unsure_trained_spam"] = data["num_trained_spam"] - \
! data["num_trained_spam_fn"]
! data["num_unsure_not_trained"] = data["num_unsure"] - \
! data["num_unsure_trained_ham"] - \
! data["num_unsure_trained_spam"]
! if data["num_unsure"]:
! data["perc_unsure_trained_ham"] = 100.0 * \
! data["num_unsure_trained_ham"] / \
! data["num_unsure"]
! data["perc_unsure_trained_spam"] = 100.0 * \
! data["num_unsure_trained_spam"] / \
! data["num_unsure"]
! data["perc_unsure_not_trained"] = 100.0 * \
! data["num_unsure_not_trained"] / \
! data["num_unsure"]
! data["total_ham"] = data["num_ham_correct"] + \
! data["num_trained_ham"]
! data["total_spam"] = data["num_spam_correct"] + \
! data["num_trained_spam"]
! if data["total_ham"]:
! data["perc_ham_incorrect"] = 100.0 * \
! data["num_trained_ham_fp"] / \
! data["total_ham"]
! data["perc_ham_unsure"] = 100.0 * \
! data["num_unsure_trained_ham"] / \
! data["total_ham"]
! data["perc_ham_incorrect_or_unsure"] = \
! 100.0 * (data["num_trained_ham_fp"] +
! data["num_unsure_trained_ham"]) / \
! data["total_ham"]
! if data["total_spam"]:
! data["perc_spam_correct"] = 100.0 * data["num_spam_correct"] / \
! data["total_spam"]
! data["perc_spam_unsure"] = 100.0 * \
! data["num_unsure_trained_spam"] / \
! data["total_spam"]
! data["perc_spam_correct_or_unsure"] = \
! 100.0 * (data["num_spam_correct"] + \
! data["num_unsure_trained_spam"]) / \
! data["total_spam"]
! return data
!
! def _AddPercentStrings(self, data, dp):
! data["perc_ham_s"] = "%%(perc_ham).%df%%(perc)s" % (dp,)
! data["perc_spam_s"] = "%%(perc_spam).%df%%(perc)s" % (dp,)
! data["perc_unsure_s"] = "%%(perc_unsure).%df%%(perc)s" % (dp,)
! data["perc_correct_s"] = "%%(perc_correct).%df%%(perc)s" % (dp,)
! data["perc_incorrect_s"] = "%%(perc_incorrect).%df%%(perc)s" % (dp,)
! data["perc_fp_s"] = "%%(perc_fp).%df%%(perc)s" % (dp,)
! data["perc_fn_s"] = "%%(perc_fn).%df%%(perc)s" % (dp,)
! data["perc_spam_correct_s"] = "%%(perc_spam_correct).%df%%(perc)s" \
! % (dp,)
! data["perc_spam_unsure_s"] = "%%(perc_spam_unsure).%df%%(perc)s" \
! % (dp,)
! data["perc_spam_correct_or_unsure_s"] = \
! "%%(perc_spam_correct_or_unsure).%df%%(perc)s" % (dp,)
! data["perc_ham_incorrect_s"] = "%%(perc_ham_incorrect).%df%%(perc)s" \
! % (dp,)
! data["perc_ham_unsure_s"] = "%%(perc_ham_unsure).%df%%(perc)s" \
! % (dp,)
! data["perc_ham_incorrect_or_unsure_s"] = \
! "%%(perc_ham_incorrect_or_unsure).%df%%(perc)s" % (dp,)
! data["perc_unsure_trained_ham_s"] = \
! "%%(perc_unsure_trained_ham).%df%%(perc)s" % (dp,)
! data["perc_unsure_trained_spam_s"] = "%%(perc_unsure_trained_spam).%df%%(perc)s" \
! % (dp,)
! data["perc_unsure_not_trained_s"] = "%%(perc_unsure_not_trained).%df%%(perc)s" \
! % (dp,)
! data["perc"] = "%"
! return data
!
! def GetStats(self, use_html=False, session_only=False, decimal_points=1):
! """Return a description of the statistics.
!
! If session_only is True, then only a description of the statistics
! since we were last reset. Otherwise, lifetime statistics (i.e.
! those including the ones loaded).
!
! Users probably care most about persistent statistics, so present
! those by default. If session-only stats are desired, then a
! special call to here can be made.
!
! The percentages will be accurate to the given number of decimal
! points.
!
! If use_html is True, then the returned data is marked up with
! appropriate HTML, otherwise it is plain text.
! """
chunks = []
push = chunks.append
!
! if session_only:
! data = {}
! data["num_seen"] = self.num_ham + self.num_spam + \
! self.num_unsure
! data["num_ham"] = self.num_ham
! data["num_spam"] = self.num_spam
! data["num_unsure"] = self.num_unsure
! data["num_trained_ham"] = self.num_trained_ham
! data["num_trained_ham_fp"] = self.num_trained_ham_fp
! data["num_trained_spam"] = self.num_trained_spam
! data["num_trained_spam_fn"] = self.num_trained_spam_fn
else:
! data = self._CombineSessionAndTotal()
! push(_("Messages classified: %d" % (data["num_seen"],)))
! if data["num_seen"] == 0:
! return chunks
! data = self._CalculateAdditional(data)
! format_dict = self._AddPercentStrings(data, decimal_points)
!
! # Possibly use HTML for tabs.
if use_html:
format_dict["tab"] = " "
else:
format_dict["tab"] = "\t"
! push((_("%(tab)sGood:%(tab)s%(num_ham)d (%(perc_ham_s)s)") \
! % format_dict) % format_dict)
! push((_("%(tab)sSpam:%(tab)s%(num_spam)d (%(perc_spam_s)s)") \
! % format_dict) % format_dict)
! push((_("%(tab)sUnsure:%(tab)s%(num_unsure)d (%(perc_unsure_s)s)") \
! % format_dict) % format_dict)
! push("")
!
! push((_("Classified correctly:%(tab)s%(num_correct)d (%(perc_correct_s)s of total)") \
! % format_dict) % format_dict)
! push((_("Classified incorrectly:%(tab)s%(num_incorrect)d (%(perc_incorrect_s)s of total)") \
! % format_dict) % format_dict)
! if format_dict["num_incorrect"]:
! push((_("%(tab)sFalse positives:%(tab)s%(num_trained_ham_fp)d (%(perc_fp_s)s of total)") \
! % format_dict) % format_dict)
! push((_("%(tab)sFalse negatives:%(tab)s%(num_trained_spam_fn)d (%(perc_fn_s)s of total)") \
! % format_dict) % format_dict)
! push("")
!
! push(_("Manually classified as good:%(tab)s%(num_trained_ham)d") % format_dict)
! push(_("Manually classified as spam:%(tab)s%(num_trained_spam)d") % format_dict)
! push("")
!
! if format_dict["num_unsure"]:
! push((_("Unsures trained as good:%(tab)s%(num_unsure_trained_ham)d (%(perc_unsure_trained_ham_s)s of unsures)") \
! % format_dict) % format_dict)
! push((_("Unsures trained as spam:%(tab)s%(num_unsure_trained_spam)d (%(perc_unsure_trained_spam_s)s of unsures)") \
! % format_dict) % format_dict)
! push((_("Unsures not trained:%(tab)s%(tab)s%(num_unsure_not_trained)d (%(perc_unsure_not_trained_s)s of unsures)") \
! % format_dict) % format_dict)
! push("")
!
! if format_dict["total_spam"]:
! push((_("Spam correctly identified:%(tab)s%(perc_spam_correct_s)s (+ %(perc_spam_unsure_s)s unsure)") \
! % format_dict) % format_dict)
! if format_dict["total_ham"]:
! push((_("Good incorrectly identified:%(tab)s%(perc_ham_incorrect_s)s (+ %(perc_ham_unsure_s)s unsure)") \
! % format_dict) % format_dict)
return chunks
From anadelonbrin at users.sourceforge.net Tue Dec 21 22:48:41 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Tue Dec 21 22:48:44 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py, 1.144,
1.145 filter.py, 1.41, 1.42 manager.py, 1.101,
1.102 msgstore.py, 1.96, 1.97 train.py, 1.40, 1.41 oastats.py,
1.11, NONE
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26759/Outlook2000
Modified Files:
addin.py filter.py manager.py msgstore.py train.py
Removed Files:
oastats.py
Log Message:
Use spambayes.Stats instead of out own oastats module. The central one has changed
to be much like the oastats one was, except that it uses the messageinfo database
and not another pickle.
We also now stored the 'classified' (c) attribute in the message info database for
Outlook.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.144
retrieving revision 1.145
diff -C2 -d -r1.144 -r1.145
*** addin.py 20 Dec 2004 04:24:54 -0000 1.144
--- addin.py 21 Dec 2004 21:48:30 -0000 1.145
***************
*** 695,700 ****
for msgstore_message in msgstore_messages:
# Record this recovery in our stats.
! self.manager.stats.RecordManualClassification(False,
! self.manager.score(msgstore_message))
# Record the original folder, in case this message is not where
# it was after filtering, or has never been filtered.
--- 695,700 ----
for msgstore_message in msgstore_messages:
# Record this recovery in our stats.
! self.manager.stats.RecordTraining(False,
! self.manager.score(msgstore_message))
# Record the original folder, in case this message is not where
# it was after filtering, or has never been filtered.
***************
*** 763,767 ****
# Record this recovery in our stats.
! self.manager.stats.RecordManualClassification(True,
self.manager.score(msgstore_message))
# Must train before moving, else we lose the message!
--- 763,767 ----
# Record this recovery in our stats.
! self.manager.stats.RecordTraining(True,
self.manager.score(msgstore_message))
# Must train before moving, else we lose the message!
***************
*** 1144,1148 ****
self.explorers_collection = None
self.toolbar = None
- self.manager.stats.Store() # save stats
self.close() # disconnect events.
--- 1144,1147 ----
***************
*** 1505,1513 ****
# Report some simple stats, for session, and for total.
print "Session:"
! print "\r\n".join(self.manager.stats.GetStats(True))
print "Total:"
print "\r\n".join(self.manager.stats.GetStats())
- # Save stats.
- self.manager.stats.Store()
self.manager.Close()
self.manager = None
--- 1504,1510 ----
# Report some simple stats, for session, and for total.
print "Session:"
! print "\r\n".join(self.manager.stats.GetStats(session_only=True))
print "Total:"
print "\r\n".join(self.manager.stats.GetStats())
self.manager.Close()
self.manager = None
Index: filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
retrieving revision 1.41
retrieving revision 1.42
diff -C2 -d -r1.41 -r1.42
*** filter.py 8 Dec 2004 04:27:59 -0000 1.41
--- filter.py 21 Dec 2004 21:48:37 -0000 1.42
***************
*** 17,26 ****
--- 17,29 ----
disposition = "Yes"
attr_prefix = "spam"
+ msg.c = "spam"
elif prob_perc >= config.unsure_threshold:
disposition = "Unsure"
attr_prefix = "unsure"
+ msg.c = "unsure"
else:
disposition = "No"
attr_prefix = "ham"
+ msg.c = "ham"
ms = mgr.message_store
Index: manager.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
retrieving revision 1.101
retrieving revision 1.102
diff -C2 -d -r1.101 -r1.102
*** manager.py 20 Dec 2004 03:37:38 -0000 1.101
--- manager.py 21 Dec 2004 21:48:37 -0000 1.102
***************
*** 17,21 ****
import msgstore
- import oastats
try:
--- 17,20 ----
***************
*** 123,127 ****
def import_core_spambayes_stuff(ini_filenames):
global bayes_classifier, bayes_tokenize, bayes_storage, bayes_options, \
! bayes_message
if "spambayes.Options" in sys.modules:
# The only thing we are worried about here is spambayes.Options
--- 122,126 ----
def import_core_spambayes_stuff(ini_filenames):
global bayes_classifier, bayes_tokenize, bayes_storage, bayes_options, \
! bayes_message, bayes_stats
if "spambayes.Options" in sys.modules:
# The only thing we are worried about here is spambayes.Options
***************
*** 150,157 ****
--- 149,158 ----
from spambayes import storage
from spambayes import message
+ from spambayes import Stats
bayes_classifier = classifier
bayes_tokenize = tokenize
bayes_storage = storage
bayes_message = message
+ bayes_stats = Stats
assert "spambayes.Options" in sys.modules, \
"Expected 'spambayes.Options' to be loaded here"
***************
*** 454,458 ****
self.ReportFatalStartupError("Failed to load bayes database")
self.classifier_data.InitNew()
! self.stats = oastats.Stats(self.config, self.data_directory)
# Logging - this should be somewhere else.
--- 455,463 ----
self.ReportFatalStartupError("Failed to load bayes database")
self.classifier_data.InitNew()
! s_thres = self.config.filter.spam_threshold
! u_thres = self.config.filter.unsure_threshold
! mdb = self.classifier_data.message_db
! self.stats = bayes_stats.Stats(s_thres, u_thres, mdb, "ham",
! "unsure", "spam")
# Logging - this should be somewhere else.
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.96
retrieving revision 1.97
diff -C2 -d -r1.96 -r1.97
*** msgstore.py 20 Dec 2004 04:23:33 -0000 1.96
--- msgstore.py 21 Dec 2004 21:48:37 -0000 1.97
***************
*** 809,814 ****
# For use with the spambayes.message messageinfo database.
! self.stored_attributes = ['t', 'original_folder']
self.t = None
self.original_folder = None
--- 809,815 ----
# For use with the spambayes.message messageinfo database.
! self.stored_attributes = ['c', 't', 'original_folder']
self.t = None
+ self.c = None
self.original_folder = None
Index: train.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/train.py,v
retrieving revision 1.40
retrieving revision 1.41
diff -C2 -d -r1.40 -r1.41
*** train.py 25 Nov 2004 23:26:58 -0000 1.40
--- train.py 21 Dec 2004 21:48:38 -0000 1.41
***************
*** 172,179 ****
# If we are rebuilding, then we reset the statistics, too.
# (But output them to the log for reference).
! mgr.LogDebug(1, "Session:" + "\r\n".join(mgr.stats.GetStats(False)))
mgr.LogDebug(1, "Total:" + "\r\n".join(mgr.stats.GetStats()))
mgr.stats.Reset()
! mgr.stats.ResetTotal(True)
progress.tick()
--- 172,180 ----
# If we are rebuilding, then we reset the statistics, too.
# (But output them to the log for reference).
! mgr.LogDebug(1, "Session:" + "\r\n".join(\
! mgr.stats.GetStats(session_only=True)))
mgr.LogDebug(1, "Total:" + "\r\n".join(mgr.stats.GetStats()))
mgr.stats.Reset()
! mgr.stats.ResetTotal(permanently=True)
progress.tick()
--- oastats.py DELETED ---
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:05:26 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:05:29 2004
Subject: [Spambayes-checkins] spambayes/spambayes/resources ui.html, 1.34,
1.35
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/resources
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11911/spambayes/resources
Modified Files:
ui.html
Log Message:
Add notes about which sections do not need to be translated.
Index: ui.html
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/resources/ui.html,v
retrieving revision 1.34
retrieving revision 1.35
diff -C2 -d -r1.34 -r1.35
*** ui.html 10 Aug 2004 14:20:25 -0000 1.34
--- ui.html 21 Dec 2004 23:05:23 -0000 1.35
***************
*** 65,68 ****
--- 65,72 ----
id
tag, and becomes a Python object at runtime.
+ This "Introduction" section serves as an introduction to this file.
+ It does not require translation and is never used in the run-time
+ interface.
+
As an example of how this works, here is an editbox with
an id
of examplebox
:
***************
*** 107,110 ****
--- 111,115 ----
presented in one of these. The pieces aren't presented in these
boxes here in ui.html
to avoid duplication of HTML.
+ As such, this section does not need translation.
***************
*** 327,331 ****
! Warning: please insert warning message here!
|
--- 332,338 ----
! Warning: please insert warning message here! The warnings are
! all dynamically inserted, and so translating this text is not
! necessary.
|
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:07:20 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:07:23 2004
Subject: [Spambayes-checkins] spambayes/spambayes i18n.py,1.1,1.2
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12321/spambayes
Modified Files:
i18n.py
Log Message:
Add a function to load the ui.html file appropriate for the language.
Index: i18n.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/i18n.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** i18n.py 2 Nov 2004 21:27:43 -0000 1.1
--- i18n.py 21 Dec 2004 23:07:17 -0000 1.2
***************
*** 47,50 ****
--- 47,51 ----
## ..etc..
+
class LanguageManager:
def __init__(self, directory=os.path.dirname(__file__)):
***************
*** 90,93 ****
--- 91,112 ----
lang.install()
+ def import_ui_html(self):
+ """Load and return the appropriate ui_html.py module for the
+ current language."""
+ for language in self.current_langs_codes:
+ moduleName = 'languages.%s.i18n_ui_html' % (language, )
+ try:
+ module = __import__(moduleName, {}, {}, ('languages',
+ language))
+ except ImportError:
+ # That language isn't available - fall back to the
+ # next one.
+ pass
+ else:
+ return module
+ # Nothing available - use the default.
+ from spambayes.resources import ui_html
+ return ui_html
+
def _install_gettext(self):
"""Set the gettext specific environment."""
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:08:54 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:08:56 2004
Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py, 1.39,
1.40 UserInterface.py, 1.49, 1.50
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12699/spambayes
Modified Files:
ImapUI.py UserInterface.py
Log Message:
Add support for i18n.
Index: ImapUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v
retrieving revision 1.39
retrieving revision 1.40
diff -C2 -d -r1.39 -r1.40
*** ImapUI.py 13 Oct 2004 02:42:04 -0000 1.39
--- ImapUI.py 21 Dec 2004 23:08:51 -0000 1.40
***************
*** 110,114 ****
class IMAPUserInterface(UserInterface.UserInterface):
"""Serves the HTML user interface for the proxies."""
! def __init__(self, cls, imap, pwd, imap_session_class):
global parm_map
# Only offer SSL if it is available
--- 110,115 ----
class IMAPUserInterface(UserInterface.UserInterface):
"""Serves the HTML user interface for the proxies."""
! def __init__(self, cls, imap, pwd, imap_session_class,
! lang_manager=None):
global parm_map
# Only offer SSL if it is available
***************
*** 121,125 ****
else:
del IMAP4_SSL
! UserInterface.UserInterface.__init__(self, cls, parm_map, adv_map)
self.classifier = cls
self.imap = imap
--- 122,127 ----
else:
del IMAP4_SSL
! UserInterface.UserInterface.__init__(self, cls, parm_map, adv_map,
! lang_manager)
self.classifier = cls
self.imap = imap
Index: UserInterface.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v
retrieving revision 1.49
retrieving revision 1.50
diff -C2 -d -r1.49 -r1.50
*** UserInterface.py 21 Dec 2004 21:37:06 -0000 1.49
--- UserInterface.py 21 Dec 2004 23:08:51 -0000 1.50
***************
*** 133,138 ****
class BaseUserInterface(Dibbler.HTTPPlugin):
! def __init__(self):
Dibbler.HTTPPlugin.__init__(self)
htmlSource, self._images = self.readUIResources()
self.html = PyMeldLite.Meld(htmlSource, readonly=True)
--- 133,139 ----
class BaseUserInterface(Dibbler.HTTPPlugin):
! def __init__(self, lang_manager=None):
Dibbler.HTTPPlugin.__init__(self)
+ self.lang_manager = lang_manager
htmlSource, self._images = self.readUIResources()
self.html = PyMeldLite.Meld(htmlSource, readonly=True)
***************
*** 250,257 ****
def readUIResources(self):
"""Returns ui.html and a dictionary of Gifs."""
!
! # Using `exec` is nasty, but I couldn't figure out a way of making
! # `getattr` or `__import__` work with ResourcePackage.
! from spambayes.resources import ui_html
images = {}
for baseName in IMAGES:
--- 251,258 ----
def readUIResources(self):
"""Returns ui.html and a dictionary of Gifs."""
! if self.lang_manager:
! ui_html = self.lang_manager.import_ui_html()
! else:
! from spambayes.resources import ui_html
images = {}
for baseName in IMAGES:
***************
*** 265,271 ****
"""Serves the HTML user interface."""
! def __init__(self, bayes, config_parms=(), adv_parms=()):
"""Load up the necessary resources: ui.html and helmet.gif."""
! BaseUserInterface.__init__(self)
self.classifier = bayes
self.parm_ini_map = config_parms
--- 266,273 ----
"""Serves the HTML user interface."""
! def __init__(self, bayes, config_parms=(), adv_parms=(),
! lang_manager=None):
"""Load up the necessary resources: ui.html and helmet.gif."""
! BaseUserInterface.__init__(self, lang_manager)
self.classifier = bayes
self.parm_ini_map = config_parms
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:10:24 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:10:30 2004
Subject: [Spambayes-checkins] spambayes/spambayes FileCorpus.py,1.16,1.17
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13193/spambayes
Modified Files:
FileCorpus.py
Log Message:
Don't use the strict keyword arg as it is deprecated (we are passing the default value,
anyway).
Index: FileCorpus.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/FileCorpus.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -C2 -d -r1.16 -r1.17
*** FileCorpus.py 19 Jul 2004 09:55:21 -0000 1.16
--- FileCorpus.py 21 Dec 2004 23:10:21 -0000 1.17
***************
*** 273,277 ****
# We parse the content into a generic email.Message object.
! msg = email.message_from_string(payload, strict=False)
# And then we set ourselves to be equal to it.
--- 273,277 ----
# We parse the content into a generic email.Message object.
! msg = email.message_from_string(payload)
# And then we set ourselves to be equal to it.
***************
*** 347,352 ****
'''Create a message object from a filename in a directory'''
if content:
! msg = email.message_from_string(content, _class=FileMessage,
! strict=False)
msg.file_name = key
msg.directory = directory
--- 347,351 ----
'''Create a message object from a filename in a directory'''
if content:
! msg = email.message_from_string(content, _class=FileMessage)
msg.file_name = key
msg.directory = directory
***************
*** 380,385 ****
if content:
msg = email.message_from_string(content,
! _class=GzipFileMessage,
! strict=False)
msg.file_name = key
msg.directory = directory
--- 379,383 ----
if content:
msg = email.message_from_string(content,
! _class=GzipFileMessage)
msg.file_name = key
msg.directory = directory
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:12:14 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:12:17 2004
Subject: [Spambayes-checkins] spambayes/spambayes message.py,1.63,1.64
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13741/spambayes
Modified Files:
message.py
Log Message:
My patch for Python 2.4 compatibility for the setPayload function wouldn't work.
This version should. (Need to update the __dict__, note replace it).
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.63
retrieving revision 1.64
diff -C2 -d -r1.63 -r1.64
*** message.py 21 Dec 2004 21:37:06 -0000 1.63
--- message.py 21 Dec 2004 23:12:11 -0000 1.64
***************
*** 322,326 ****
DeprecationWarning, 2)
new_me = email.message_from_string(payload, _class=Message)
! self.__dict__ = new_me.__dict__
def setId(self, id):
--- 322,326 ----
DeprecationWarning, 2)
new_me = email.message_from_string(payload, _class=Message)
! self.__dict__.update(new_me.__dict__)
def setId(self, id):
***************
*** 422,426 ****
DeprecationWarning, 2)
new_me = email.message_from_string(payload, _class=SBHeaderMessage)
! self.__dict__ = new_me.__dict__
def setIdFromPayload(self):
--- 422,426 ----
DeprecationWarning, 2)
new_me = email.message_from_string(payload, _class=SBHeaderMessage)
! self.__dict__.update(new_me.__dict__)
def setIdFromPayload(self):
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:12:59 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:13:00 2004
Subject: [Spambayes-checkins] spambayes/spambayes message.py, 1.49.4.6,
1.49.4.7
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13913/spambayes
Modified Files:
Tag: release_1_0-branch
message.py
Log Message:
Backport:
My patch for Python 2.4 compatibility for the setPayload function wouldn't work.
This version should. (Need to update the __dict__, note replace it).
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.49.4.6
retrieving revision 1.49.4.7
diff -C2 -d -r1.49.4.6 -r1.49.4.7
*** message.py 20 Dec 2004 04:09:28 -0000 1.49.4.6
--- message.py 21 Dec 2004 23:12:55 -0000 1.49.4.7
***************
*** 248,252 ****
# 1.0.x branch, so use a different ugly hack.
new_me = email.message_from_string(payload, _class=Message)
! self.__dict__ = new_me.__dict__
def setId(self, id):
--- 248,252 ----
# 1.0.x branch, so use a different ugly hack.
new_me = email.message_from_string(payload, _class=Message)
! self.__dict__.update(new_me.__dict__)
def setId(self, id):
***************
*** 342,347 ****
def setPayload(self, payload):
! new_me = email.message_from_string(payload, _class=Message)
! self.__dict__ = new_me.__dict__
def setIdFromPayload(self):
--- 342,347 ----
def setPayload(self, payload):
! new_me = email.message_from_string(payload, _class=SBHeaderMessage)
! self.__dict__.update(new_me.__dict__)
def setIdFromPayload(self):
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:13:50 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:13:52 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_server.py,1.33,1.34
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14133/scripts
Modified Files:
sb_server.py
Log Message:
Add i18n support.
Index: sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v
retrieving revision 1.33
retrieving revision 1.34
diff -C2 -d -r1.33 -r1.34
*** sb_server.py 20 Dec 2004 23:24:03 -0000 1.33
--- sb_server.py 21 Dec 2004 23:13:47 -0000 1.34
***************
*** 55,58 ****
--- 55,66 ----
True, False = 1, 0
+ try:
+ reversed
+ except NameError:
+ # Maintain compatibility with Python 2.2 and 2.3
+ def reversed(seq):
+ seq = list(seq[:])
+ seq.reverse()
+ return iter(seq)
todo = """
***************
*** 102,105 ****
--- 110,114 ----
import spambayes.message
+ from spambayes import i18n
from spambayes import Dibbler
from spambayes import storage
***************
*** 111,114 ****
--- 120,124 ----
from spambayes.Version import get_version_string
+
# Increase the stack size on MacOS X. Stolen from Lib/test/regrtest.py
if sys.platform == 'darwin':
***************
*** 652,655 ****
--- 662,681 ----
def init(self):
assert not self.prepared, "init after prepare, but before close"
+ # Load the environment for translation.
+ self.lang_manager = i18n.LanguageManager()
+ # Set the system user default language.
+ self.lang_manager.set_language(\
+ self.lang_manager.locale_default_lang())
+ # Set interface to use the user language in the configuration file.
+ for language in reversed(options["globals", "language"]):
+ # We leave the default in there as the last option, to fall
+ # back on if necessary.
+ self.lang_manager.add_language(language)
+ if options["globals", "verbose"]:
+ print "Asked to add languages: " + \
+ ", ".join(options["globals", "language"])
+ print "Set language to " + \
+ str(self.lang_manager.current_langs_codes)
+
# Open the log file.
if options["globals", "verbose"]:
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:16:40 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:16:44 2004
Subject: [Spambayes-checkins] spambayes/spambayes ProxyUI.py,1.54,1.55
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14826/spambayes
Modified Files:
ProxyUI.py
Log Message:
Add i18n support.
Index: ProxyUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v
retrieving revision 1.54
retrieving revision 1.55
diff -C2 -d -r1.54 -r1.55
*** ProxyUI.py 8 Dec 2004 03:34:29 -0000 1.54
--- ProxyUI.py 21 Dec 2004 23:16:28 -0000 1.55
***************
*** 164,168 ****
global state
UserInterface.UserInterface.__init__(self, proxy_state.bayes,
! parm_ini_map, adv_map)
state = proxy_state
self.state_recreator = state_recreator # ugly
--- 164,169 ----
global state
UserInterface.UserInterface.__init__(self, proxy_state.bayes,
! parm_ini_map, adv_map,
! proxy_state.lang_manager)
state = proxy_state
self.state_recreator = state_recreator # ugly
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:18:35 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:18:37 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.102,1.103
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15251/Outlook2000
Modified Files:
manager.py
Log Message:
Pass more information to the stats manager.
Index: manager.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
retrieving revision 1.102
retrieving revision 1.103
diff -C2 -d -r1.102 -r1.103
*** manager.py 21 Dec 2004 21:48:37 -0000 1.102
--- manager.py 21 Dec 2004 23:18:32 -0000 1.103
***************
*** 457,463 ****
s_thres = self.config.filter.spam_threshold
u_thres = self.config.filter.unsure_threshold
mdb = self.classifier_data.message_db
self.stats = bayes_stats.Stats(s_thres, u_thres, mdb, "ham",
! "unsure", "spam")
# Logging - this should be somewhere else.
--- 457,468 ----
s_thres = self.config.filter.spam_threshold
u_thres = self.config.filter.unsure_threshold
+ fp_cost = bayes_options["TestDriver", "best_cutoff_fp_weight"]
+ fn_cost = bayes_options["TestDriver", "best_cutoff_fn_weight"]
+ unsure_cost = bayes_options["TestDriver",
+ "best_cutoff_unsure_weight"]
mdb = self.classifier_data.message_db
self.stats = bayes_stats.Stats(s_thres, u_thres, mdb, "ham",
! "unsure", "spam", fp_cost, fn_cost,
! unsure_cost)
# Logging - this should be somewhere else.
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:19:47 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:19:48 2004
Subject: [Spambayes-checkins] spambayes/spambayes Stats.py,1.11,1.12
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15500/spambayes
Modified Files:
Stats.py
Log Message:
Include the cost figure (and a 'savings' figure, which is sure to be popular and meaningless
) in the stats.
Index: Stats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Stats.py,v
retrieving revision 1.11
retrieving revision 1.12
diff -C2 -d -r1.11 -r1.12
*** Stats.py 21 Dec 2004 21:41:49 -0000 1.11
--- Stats.py 21 Dec 2004 23:19:41 -0000 1.12
***************
*** 53,57 ****
class Stats(object):
def __init__(self, spam_threshold, unsure_threshold, messageinfo_db,
! ham_string, unsure_string, spam_string):
self.messageinfo_db = messageinfo_db
self.spam_threshold = spam_threshold
--- 53,58 ----
class Stats(object):
def __init__(self, spam_threshold, unsure_threshold, messageinfo_db,
! ham_string, unsure_string, spam_string, fp_cost, fn_cost,
! unsure_cost):
self.messageinfo_db = messageinfo_db
self.spam_threshold = spam_threshold
***************
*** 60,63 ****
--- 61,67 ----
self.unsure_string = unsure_string
self.spam_string = spam_string
+ self.fp_cost = fp_cost
+ self.fn_cost = fn_cost
+ self.unsure_cost = unsure_cost
# Reset session stats.
self.Reset()
***************
*** 229,232 ****
--- 233,245 ----
data["num_unsure_trained_spam"]) / \
data["total_spam"]
+
+ data["total_cost"] = data["num_trained_ham_fp"] * self.fp_cost + \
+ data["num_trained_spam_fn"] * self.fn_cost + \
+ data["num_unsure"] * self.unsure_cost
+ # If there was no filtering done, what would the cost have been?
+ # (Assuming that any spam in the inbox earns the cost of a fn)
+ no_filter_cost = data["num_spam"] * self.fn_cost
+ data["cost_savings"] = no_filter_cost - data["total_cost"]
+
return data
***************
*** 345,348 ****
--- 358,366 ----
push((_("Good incorrectly identified:%(tab)s%(perc_ham_incorrect_s)s (+ %(perc_ham_unsure_s)s unsure)") \
% format_dict) % format_dict)
+ if format_dict["total_spam"] or format_dict["total_ham"]:
+ push("")
+
+ push(_("Total cost of spam:%(tab)s$%(total_cost).2f") % format_dict)
+ push(_("SpamBayes savings:%(tab)s$%(cost_savings).2f") % format_dict)
return chunks
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:21:25 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:21:27 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_pop3dnd.py,1.13,1.14
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15878/scripts
Modified Files:
sb_pop3dnd.py
Log Message:
Pass an existing message info db to message objects where possible.
Correctly initialise the (new and improved) stats manager.
Index: sb_pop3dnd.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_pop3dnd.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -d -r1.13 -r1.14
*** sb_pop3dnd.py 22 Nov 2004 00:16:39 -0000 1.13
--- sb_pop3dnd.py 21 Dec 2004 23:21:22 -0000 1.14
***************
*** 120,125 ****
__implements__ = (IMessage,)
! def __init__(self, date=None):
! message.Message.__init__(self)
# We want to persist more information than the generic
# Message class.
--- 120,125 ----
__implements__ = (IMessage,)
! def __init__(self, date=None, message_db=None):
! message.Message.__init__(self, message_info_db=message_db)
# We want to persist more information than the generic
# Message class.
***************
*** 289,295 ****
class DynamicIMAPMessage(IMAPMessage):
"""An IMAP Message that may change each time it is loaded."""
! def __init__(self, func):
date = imaplib.Time2Internaldate(time.time())[1:-1]
! IMAPMessage.__init__(self, date)
self.func = func
self.load()
--- 289,295 ----
class DynamicIMAPMessage(IMAPMessage):
"""An IMAP Message that may change each time it is loaded."""
! def __init__(self, func, mdb):
date = imaplib.Time2Internaldate(time.time())[1:-1]
! IMAPMessage.__init__(self, date, mdb)
self.func = func
self.load()
***************
*** 303,313 ****
! class IMAPFileMessage(IMAPMessage, FileCorpus.FileMessage):
'''IMAP Message that persists as a file system artifact.'''
! def __init__(self, file_name=None, directory=None):
"""Constructor(message file name, corpus directory name)."""
date = imaplib.Time2Internaldate(time.time())[1:-1]
! IMAPMessage.__init__(self, date)
FileCorpus.FileMessage.__init__(self, file_name, directory)
self.id = file_name
--- 303,313 ----
! class IM.APFileMessage(IMAPMessage, FileCorpus.FileMessage):
'''IMAP Message that persists as a file system artifact.'''
! def __init__(self, file_name=None, directory=None, mdb=None):
"""Constructor(message file name, corpus directory name)."""
date = imaplib.Time2Internaldate(time.time())[1:-1]
! IMAPMessage.__init__(self, date, mdb)
FileCorpus.FileMessage.__init__(self, file_name, directory)
self.id = file_name
***************
*** 538,543 ****
class SpambayesInbox(SpambayesMailbox):
"""A special mailbox that holds status messages from SpamBayes."""
! def __init__(self, id):
IMAPMailbox.__init__(self, "INBOX", "spambayes", id)
self.UID_validity = id
self.nextUID = 1
--- 538,544 ----
class SpambayesInbox(SpambayesMailbox):
"""A special mailbox that holds status messages from SpamBayes."""
! def __init__(self, id, message_db):
IMAPMailbox.__init__(self, "INBOX", "spambayes", id)
+ self.mdb = message_db
self.UID_validity = id
self.nextUID = 1
***************
*** 546,549 ****
--- 547,560 ----
self.storage = {}
self.createMessages()
+ s_thres = options["Categorization", "spam_cutoff"]
+ u_thres = options["Categorization", "ham_cutoff"]
+ fp_cost = options["TestDriver", "best_cutoff_fp_weight"]
+ fn_cost = options["TestDriver", "best_cutoff_fn_weight"]
+ unsure_cost = options["TestDriver", "best_cutoff_unsure_weight"]
+ h_string = options["Headers", "header_ham_string"]
+ s_string = options["Headers", "header_spam_string"]
+ u_string = options["Headers", "header_unsure_string"]
+ self.stats = Stats(s_thres, u_thres, message_db, h_string, u_string,
+ s_string, fp_cost, fn_cost, unsure_cost)
def buildStatusMessage(self, body=False, headers=False):
***************
*** 596,601 ****
msg.append('\r\n')
if body:
- s = Stats()
- s.CalculateStats()
msg.extend(s.GetStats(use_html=False))
return "\r\n".join(msg)
--- 607,610 ----
***************
*** 612,618 ****
msg.date = date
self.addMessage(msg)
! msg = DynamicIMAPMessage(self.buildStatusMessage)
self.addMessage(msg)
! msg = DynamicIMAPMessage(self.buildStatisticsMessage)
self.addMessage(msg)
# XXX Add other messages here, for example
--- 621,627 ----
msg.date = date
self.addMessage(msg)
! msg = DynamicIMAPMessage(self.buildStatusMessage, self.mdb)
self.addMessage(msg)
! msg = DynamicIMAPMessage(self.buildStatisticsMessage, self.mdb)
self.addMessage(msg)
# XXX Add other messages here, for example
***************
*** 912,915 ****
--- 921,927 ----
self.DBName, self.useDB = storage.database_type([])
self.bayes = storage.open_storage(self.DBName, self.useDB)
+ if not hasattr(self, "MBDName"):
+ self.MDBName, self.useMDB = message.database_type()
+ self.mdb = message.open_storage(self.MDBName, self.useMDB)
self.buildStatusStrings()
***************
*** 943,947 ****
"spam_to_train")
spam_train_box = SpambayesMailbox("TrainAsSpam", 3, spam_train_cache)
! inbox = SpambayesInbox(4)
spam_trainer = Trainer(spam_train_box, True)
--- 955,959 ----
"spam_to_train")
spam_train_box = SpambayesMailbox("TrainAsSpam", 3, spam_train_cache)
! inbox = SpambayesInbox(4, state.mdb)
spam_trainer = Trainer(spam_train_box, True)
From anadelonbrin at users.sourceforge.net Wed Dec 22 00:06:23 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 00:53:50 2004
Subject: [Spambayes-checkins]
spambayes/spambayes/resources ui_html.py, 1.33, 1.34
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/resources
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12120/spambayes/resources
Modified Files:
ui_html.py
Log Message:
'Compiled' version of new ui.html which just has more comments, no new functionality.
Index: ui_html.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/resources/ui_html.py,v
retrieving revision 1.33
retrieving revision 1.34
diff -C2 -d -r1.33 -r1.34
*** ui_html.py 10 Aug 2004 14:20:25 -0000 1.33
--- ui_html.py 21 Dec 2004 23:06:19 -0000 1.34
***************
*** 6,155 ****
import zlib
! data = zlib.decompress("xÚÝ=ksÛF’ßS•ÿ0Á–\015©–\")Év\022=¸kËNœ;;öÙÊ¥¶R)Õ\020\030’X\000‚‡(nnÿûõcf0xð!\
! ;»WuJÊ\"\001LOwO¿§\007ºøê廫뿽%^_¿}#ÞÿôâÍ\017WÂ;\032~>½\032^^¿ä\033O†ãcqˤ\
! ˆÊ(Md<\032½úÑ›|ùÅÅ¢\\Æô[É\020—Q\031+\021…—\036}ò&\0373¹|!ת\020?\025*\027?$¥Êg2P\027#z\000Ç,U\
! )Å¢,³#õ[\025Ý]zW2X(O\004)<œ”—^’\036\005|iÔûü«û,ÊUጸ^T\003\0018ÿ‡LÄñ·_Åx|Fÿ‹ï\
! ß^o\002CÓ\036]\001Œ<{¦\037ˆeU”G¹º“q\024Ê\022.À¢LsƒYQ®‰¤i\032®Åïb\006\020ÎÄ·ãGBæ‘Œ\007¢XE\
! E1\020\013\025ß©2\012ä¹XÊ|\036%gb,þùå\027¥œ\002óö\030‡\017ÏÒ|\011Ï6\000|ù…<‹£ä\026®\007iœægâOcø\011è¦<\
»‹`ýT¸áî\"½ƒ\005ªï=\033?\031Ïfxo¨Y!¤Fîh¥¢ù\002pœ¦qHOÜEjE3w\036I\000O\0313véL\022šd*ƒ\
! ÛyžVI\0103\005c\0053‹L†a”Ì/ŸÚG±š\001Œã§ç_~!ìÏ4ÍC•\037•i\006·²{Q¤°\036b\032\003̾ç¦iY¦\
! ËΣ„7Ê-!D8\027Ñ?\024ïAÄåÈNΘÉû\006õ¯¼FK‹¼ž\036@Ž³{½è 0 \035ÌéBô\
! Jè˜d°ód¿Àê‡k‘Û\002Ñyh\0330¤£Ì£Lݤ\011/hƒ¹*ÀÿôƒUô\032ŒÞë>Á9\031?:·Ð¿!èçýl»\030\031\
! [q1Ò¶\023>\022\023Ñxâ\007®!ã/ÂèN\004±,ŠKŸ5ÈŸè;E\006\026\016G€m\000ƒæM.¢å\\\024ypéó•á<šù\002\014Ö\
! <¹ôå´XFa\030+4yœL‹ì\034ð\000\010\015hz\"^\0053Qs²,OÃ*(”ˆ…ÿYM]\003OÏœñ’1M—\012—Ä\023‹\
! \\Í`\"øîO^ÿ\027#Ù?\035èZBs=ž—碊†èvš¨7¿\000Ø£È9\004±”QrÅêìi\012=ÞÌç‹Å“É\017hù\
! ‘0gX–'õ\002d“ëETˆY\024ƒÑ¿\010ÒPM,\026ôm B5‹\022`B¹P\"NÓÛ#™„G3¥b\006‘ÎèN…N02<2W\
! k\026~T9Xá¡\020×pù\016ÌZ\025\014 ‹@\006\013\034A.™§\013Á'ä\012Ü„\022ê¾Ìe€\002\016\023ƒÁJ¢¬Š%}/\031B^%e´\004\
! ƒ•Šp\015ü\004G\022ÇkÁ‹©\010\021\002].dÙD‹Ç3n¢À_…¨2q\004Oi®\010˜\0324\026þ\007\023\020«²T\006S\036\033¤Ë,M€Û\005\
! ÐÆ\014|¿~«âð\015è¤æ¡ˆ˜{KÀ\007í*¢\001ÈÝE¡ÁÀ \010¦ÿ(þ\035,\017\020šeh\016cÀ3ç1Ky\013ø\001mŒ^–\026\
! E\004ÆvÈ0^+gfZ^Ò¨D©°@ÞLUƒ{\013$*Ñr@xF¡A¸”ó\001±{ª€@œT¼_—\013°!\032;€«¹>¼\030e\
! ®<=G¨°hr™Å„É\"]1¾«4¿%g\017‹\032ñSaTNÓ{p\010å‚\001ÀÅ6.\000‚/i˜0@ßÒ*x\021%YU’6Ôx\
! ¢\\g\012¢5\020\037\017\004.®”½í\0114k—ÞñØ\033iͪ×LÀ\"\027bV\016³j\021‚e›çr¹”%\013ÙYƒzTJ2`κîï\
! Ù\030ÿ³\026.\0036˜‡û\034ž\015€Î]çc‡£õe“|é‘Á¿\032{\023ø¹\030á\0359˼t\010Ûs\034꾸\024i¦’\003OÛ\002o \
! ¼¼ô\016ÁÉðàpOHa\032\000 \032!Ú)0W*?@ û‚Ér0+\010lX¯®YøÉã¸<ï_~»ÄÛ\004a„v÷b\024Mö'\
! ÉÁbHЀFïj!“¹\012½ÿC’\014\012m’F g\035÷¡uõcJ’U\033uŠìD\025/·\"ß²ìè³\012À]\025‰¯í/Z\030\034\
! 8“\020VËi\012¨g²(Ñzað‘ÌÑü@T\011š?\020iÎ\023à]‚_[P†6ËÓ¥(‚\\–`Ï\"F\013lëó$ìº ´#\001ç5\
! 1à\000Š¦ÀðO×ƪÂm\000Íó5?\022\001ò\011'G`¤ÅB©r JL\003AÛñ\036›j÷†Xƒ\021Næ\025\022\016V‘T\021\014b¢\
! Vµ¹m1鈬4;ÁBYl\023èÒÀÛ¬…’źm<_¨\030¬%>ÂV¾ãWkfÁ\023)à¦\023\037Œò^K^Ø\0310\022È)™\
! ËLœà\006|Þ„â¢ð\005ZW\007''\016Ó·=#M\034éë¨Êþ}\021¨8†‘\001&=~m³Ê|r\001A¨\001\007w½Öx}ÙGíÁ©\
! /ý\023\010¡hjA†¿\014A‹\000N\007¢\037 \\Á¼>j\004E…Gù-ø\030úœ€\000è±\033\005²úAi€ù\034n\026¥,«‚ÃM=\
! ðÉØ\0077†/}61§\023â!žÎWM4PЊÕ\032x9#(*C')\034ƃ\026¼M‹²?ä*L \005ÒSÃ\001å/`:à\036h\022ª\
! ‚‘I\035‰Õc@››O;¢Ë™Õ=<ÈÎ;é\015\025Ñ\004È»\024\\]Xe1øG$ÌÄ\"Cc‚Ì\002Â?(+šf\035´OóQ;îÝ)\
! ·H\006¤\004\031Y»6>Æ;xãó¥v· Ú\011\027\032µ:ü}\015W7\011ï\003ÄUäéŠg;ÝGt\021—†àžœÔ‚{z²¿à\" \
! ö6ÛÅöcšçë\001Š\016Åx(S2Y‹\"SA4‹\002^'y'£˜Va–êÈVƒ\033ö\"ÒbY\023;-\022\016>*.r1šì\013\001é\
! #§5æ¹Â i\027•?Ì(>œB¶§î”Ž\025×\020Pß!I\020ÅÚ\0020Ô\023+\001äø\032ª)±ì\024C\014Ù\005¨\016ÆÉ\010*\000ƒ\
! \013af\000Nu`/Èâ¶/sÁ\037È\000cJ\024\"0\015ÈQ@\035>Ê$@%ÿ1-Uø,ÕrŠ\025\010²\002`^pŒƒ\021º¦4†¸^áC\
! 4\012|â\012ÌÌo•*\025…¶\014Q\016~Z\001dÈ\000\006àwm&ÁA<\001š\000ýz¬X¢àAnq«ò¨ÄŠêjn\031-G–)‰\
! \011â\017³\032\022’XýVad\036¨\034ãGF\014oµ9N’—à-¿i\004Ó)È\033\004\006˜¿å@ýL\024Õt\011\030p \000œ¸‹$q\010ryÊ\
! Þ=, žF\005P\020(`ë\\\015\023UŽ0\027½Uùè/\030¯g7 <ÏŽ¿\036Ÿx\015%À!ßá\020AS ´.JµÄBÀ\000×»€¬\
! \012yW˜°\000Â#\024\0060î\016#)Þ%\032€mË!ã€d$\0072€\005ÓªX\013\006\000+¾BªéaHV\000Æ]”Wè>jH3\010›B\023õ\
! \000\035\0347LÓ9<§‹~\020*LQo¬)\026£†CÒi›Y„(ÁB-ÛxXDÌ=UŠ)Ÿ‰\006o\023\020hô:˜\010\011\023OáOQÍçˆ\
! ¦‘x–‹YtOÆ\002W+VĶ¯S¶\030˜ø¨\\Ù¥¯‘\014ØœØÁjàT\037P…ìÊÖz>\011 ¡)]Ôó1GpµH£\
! `¡Uû`‘¦\005‰¬BÕ³‹D,\0053RC«\012#^°€1Ä{J¨¤A†‹=8c`m„ÁK†c\023\017Þ¡a]E…bÅ\002o\015\
! Á`AË\022G·\030úršïéÙJph€…\030\016‡¼m6®µ/v*LÛ½.Å1÷å\021Ä\013¸wrçø`7¬#^á*•8œÃoXJƒ\037Z‘aÃ_±±§\000¶Êp\027^DeA\014˜JÖiˆ^b$‘ø7ÚÔµƒ}cø\
! °Ò\014\031y‰{\033XÚês58¬…OZÅ!\004.Rµ#1\015ªgTLWÑmäæàø\030eÂ:´ˆUQè*Eí‡U°H\"ÌZ\
! ÀÊ™±\012¡'&(¸Ðé-„?\006û&Ûk\021?À«+\010ƒMòÔP¸,®8\035 Ê\"w¡\000{\0200ì\011˜«Då\030á\037j§Áâ\
! v\025W\0348ˆÔƒ ¦!W¤—{m\"\015ƒæ\001z\034\022ožÓºÖÆ0-%‡À \011'ÇÈf\010;.V¸O\013³¢b¥rPËã\
! JF”xá\032plÇnÓH)Ç8è\035ñ÷Ú8Tg10P3ä¿G\020)0ñ¥\\\023\007\020\030@Æ{?¢½2×µ2\026F\033Ë4sµ‘\014$\
! ŽYâ\012¼/ygRQ\010ü).`<\012“³¢õã\020VSN›&:ÃZ9ä‚^»Ž\014\030^ûR\010zdU¦vC\011¥€}9±ÀÚ\032ô\
! öç”'‡¡qeÎ,m\023ôAAz^,úŒP‹ì!KNÖ\012þ;Q;–A\013P'ø\005¨DA±Gà®Çt‚È[¥²‚scDE§”\
! ø˜ë¸8°nz`*IänÀ\013Rñ\035{8]\014…Ìr`½\"Œæb\001·A~\023pò\013\030|9S°+\013M,†žn&ó¶*\\±E\
! Œ×µDÙ‰{ÉÂqI…\005\031dÂn40mÀ¨i@±\021\"ĹË\000ÃX\004¶$\017ª\013¼b…\011û\014Ì\026\012Ê\\–$ºÀ©,å„à\
! „\002C`\032h\020@H4¶93Ï\005\021\034ŒYi•rcâ;‰‹,\016·{'ò\027\\\0004ë\004K‡[âÆJ>þJËsc§…|…œ°\
! u‹\035k§é‚EJ\023X\014í4`\035’†ÀšqŠÎ¹{ÀxU‚\013\004\010®\016„Ã=´\003{=n °¼_{Ôñ±‡r¸C4‡®À\
! \012\033;˜-Ì$ÕrÎjZ×þµeì‰&ɱt¤Ï\030\020ãÄy‹\003EÇÍž‰hkgiŒ›RØÙRp\024B6Ï\025\027\014Þb¬¬‘\
! \026‚ã›Eó*çòEK\010t\021…ä»^\021l5$w#Å\022\033\002Ð9OïÅÁ»ªÄ\024Z¼ºGºÀïá&\036c¯\013(&Vó§U|ë³\
! ŠRþЊém\000!Í´\0150¸¡*\"'˜7™…ÉmÈT\012qe¬,Žfq|5yÅŸ©=@=\003Éb/AQ3\002\007Á‹+*Ãé\
! V0N³´3OúÂHT½×˜ø !×^ÍÞø˜é;\010…§nóû\015ä²X1\032\030†p)£ŽèÛŒ4qÂÀ”Ù\034n\000\003^qöV\
! \023Wë§öYD-,\"\002Q´WHvœ\006ÒåÒ,Š»\021¬Ž&ùY\024\023»\020\034þ0ÂÄ\010\026[¦‡‹\035XnNÈqR´EfY\025A\
! \036M7Ô\\È€b·M\006¡\005x…Ù\011îyG\030ÓWe‰—\024ô\031kVûž®ý‘¬õ \027yÙRÄu+èaöý\014°\021ÒU*\
! _kŽ‚-ˆ €Zã:¡m0s\023\036•.`.È(gœ “º[¦^c\004cRí¬¸}‡\003õº\024ª¸hDùSx‡;\016„\022ñä\
! 7ÄkÐÌ'õŒ\\\027…\005\0121ø'§”«9\026·…bEÇT¬/_nC¢íŽz{©–E^X,¹Ðzš\"\025'%YÄe±M.\027r\
! dãMÎ[\014ÓŒ]àÌ\033©\003'\021GE»¦‹…/òŒ\030\017踡Ö:d¶ÙðsÆ&›4ĉv5±N–;0\006„”Ì@Å}*S\
! oÑNÃñ\030?¼\024\007\\¨¬‹5\035©\035f4ª Ì\017ʆÞv\"1Þ~1ÚÖÞAF±OBÉ¢êDË),¨ÖóS‰Lq\033H\
! ½Š\007rVr=ks)í°\026·$¥¤\003T€„\021à,{ÜøÖ\012÷s$æ\010\014R¬Â# †vSýzo µÑìì*Ø. |©ë\012—\
! \036ï7™ýÅ¥*\027)\014úþÕµ»stñÕÑ‘xË›\012Ü6ƒìYa.‰©x:›a\007Í\024ËÍXº\033ØU^‚›Ç6vgó\001w\000\
! ʳö\0361õ\025aËé¥\007¸Äë£25}E§cÛKTÊ£¥Z«ü¯Ñ¢šC6=Lþáé^5O7oŸ<{ša“œbÍöþÒ\
! {æ5ö‹:ÓMƒ g¦O\002[(*ƒ0Ü,Ò\034lž7é¶v_¤\031Ùc=\031wì{\020ˆâï‹\021ßÝ=®ÊçÔNû\023ýÞ0\016\
! R:B˹|tÔØj~hãÁÚ\017&\037±º„\021§QÒv¿Áfx¼Áý\035hÜ\031\017ÚðDïòº½h¼$¨º7§æî‚£Ž8\
! 7>CÒÆM‘x(™\037Ù„~\002¥L6Á†¶gš6{YSöž·1u\030>½¡îâüLü¢÷7Á”/—¸o‚\033«¿¶‰~òt\
! Ü$úôóˆÖ\"ñ ¢qIÁÂC\030“Ë\014\024m±.°FaVY\033B¦^¹¡\006\014\022Kæ\014Æ{ܲ¸ƒ¾'\036’£gü\014BŸ—%\
! –ÉߤsŠ%?Yžq°¡T\022Ìö‚ó\0235\005_;Fm¥2Bƒ^!]y›plô\027i\036ÒÆ+•ÈÏ\002t¶¹·\022žÃ\"‡f\
! ÂÛ‰_£9\013ÛVóåC|''”FB6D4ݼÆïž>°CJíZAóÝœœ‚ïÍ®©Þó\024\014ÙÛt´ã~ýR•à]‹\
! 6¿Þ¿{JQÎ\032÷\017ÍžÖÅtÒ\032þ\036œwñ‘ZZ½ÉñññXw©]Œ¦“A\023$=O\031FÚ\004ÄvÀBÑý½X²v€\015\
! [ëY÷Å™Ÿç\001mº\023æp#HÊ=Š³\036wæÎ/iàG\035d{\023—†aÏDÝ\0318\000.\030®éÊ´”ñ~³5ŽºÐ‰\006¬\
! º\026n\035jËÌY“j‰\021lkB[ˆƒíÃ^wG-v\016ú‰jca\\0k\023yüàΑÂ$\006-J\020ó³Ö\034ˆy\023~sÌ\
! ëîE{D›ÝfóDWiߦ¹›¥\016‡ÃÖ±¥æI¤ºƒ\004\014—>Ç û3uïë6\"÷ßÖ\030·íÑÆžóâ:Ò¶Æ[\
! sü\013Ï\004ú£^µo\024£Þàé,ç±.ŽÝM\022f\000éÞ\033› µ¸º\017Hî\005³íAª%\\z]4ý>¶ÚÔ$è.W9\031vV\
! fcãñ\033õ1\022µôÚ}”ÆÍœú (ĺ•Ì\023²QMü~æËg¦¥)JÀ¤AF—m2ŠaÍW.ŽÓM\010Z_³Óp\
! \002yí´\027qß<¡[ß4\030_7ò_¶ïTj,º{û\0216¡ªBsÕ,:W\015Ý\"‘ÂÎ\004Ü\033£Â`êô—ðH»¨õ:2b¾é\
! w2ÏÓ6¤Ý1{0ûs\001™ÖߪÏ\017\020W×Äâ2î\012Љ:m}LÁÂ\026—Òš`}ºÍR%·…ÒÕI·‡BW¢Á£Íö é0Õa˜éœŸ§=órÂAÙNPàÑ++[\007˜\003ؾÙÖŽ*ïvšÌ]\037ÛÆM\
! Y]\034APº\\†ýd\005÷¡q‹JaëMXä¼W»\013\003½¥ûùó!×÷#š¶Ï\037Hð&§×>YÐ=\003bÓŒÚÖ¶-È£Ì\
! …ùÏèïòNòU2\032]|õËÕËç×ÏÑgȪ„u7MØj\036àHzßD\031,®S}öð÷\032Ÿh†ýpA…u´!×ã\037\
! ?\026Í+ÃX%sÈæ'—âä°\036ú{“‹¤e—¡¿\034ÿzÞyL\034Dðàø\\D₆é\011àŸÿ|Ø|ü÷îRQ³\004\036U\
! \007 8ú—è×!®ôn\034øgþaÏë\016P;ÒPty)N‘^{\003ðÅ‹´-öøq¿˜˜9õ)ÈKËÝa™¾IW*¿\002G\
! ypxØ\035ýûv€džÁÞÃüy¥zhøgó’óUÔ¿F£_Ï\023‘°ìi\015ß¿ûxíOÚ©«oêuœ§»oËØ×@\
! Ùz1Ïø±š\032·¾á|\012Dc\023[uonÉã\013nζ\004VMP&<ð¹|)u¶\020Ñxm\005\033\017þòºó\016…NV¡›\
! ¶A\030}÷\015áŒÊy\020\005ÐÖ麹\016“);õ×–}i%“nxÄýÛ7‹\006çwÒ?iÑö\027tÍ—\006š‡\013õAÑ«\"';\
! ±ÚÈì$¥²\033Ø[\004ÜŸ\"xµ\021<«Mrá\017 Äâp\010Ì€WwÚAÐ9ú4h\024°\021,n\007þdH\020”\021\034Ú×þd(\030\
! Ô\021\030Þ\005ïÊÃt²ù\\[]Ž\011 \037øc$@‘\030|ÄO{É@#\017ââš[\037dð\037ÒU}$ͼͥÿ\005&-Ýþo4ÍE\037\
! ]öHoNÏôîžX‹\016w“æ~ˆ‚E¤ÄëˆúçñðzNWþê4câ‘ô>\035n)CÏB¹èb\007Š\012_;H›üȼ\014Ç\
! ë7HT‡¢ý\011^´ú4I\0179êÌÉ\031qŸ–_` ò4¸ýJˆoŽ\016ÿ\020k¤i²¶\010ÄX–\003q|,>ªLœŒÇ§âøÛ³\
! ñéÙñqÏ™L]÷àê¯ß9ÅÙ\011Às\031F©‰\003M‡Ç\031ùÿ|õ·þ0\020ÑÓg\006LñØ×ßm\010Øý÷_\004š\033¯ŽJ\
! éû¿\027\005¬¥¹1|ÿ÷\"@õ?\007\001üî&;\017î²yóÚ\0023\036ŽÇz†h§ãñoo¢•Ç j\024È~çÞc2âÿÓy\
! \037\0227\024wǘ®i¹¥bE;ê±Ln?è½\0217çØñµ\013‰\012\010{@ÚCÉz*ä[R.]ôЋÆß6¬ZoE¬“\0269Çâw\
! Ö‰ª,NeØ\000 Gøžgã_âPCü\032‘°PO\021}Ë*.#}ÔÜÖ°K€À<Ãiø\\\013´Sßðy·Ù?\0357\015Fk\017§Ïô\030IèoásV±¶=õî nã\
! km\017ô[<2Ò²ˆ\002C\015±è\006±ñ›þNgô`Lùƒ/ºžN£M\000}Ðb*µ‚÷7u\024»\006Ã¥¼¿1\007Uë”ùä{Ý\
! †!C\036\001¿‰åTÅÞä\005~\021Z.úBѦžVQ·/öÑ®I14™Ñ¥üá´n£ÑÌjÈüYÿdJs5W÷[Ȥû\
! ÿ^\032iJCà\007Ý;úÊöŽ~2©àð1^\012daš?|ç’¥¸qé&Á\006§æoFÚ\031ePÿ.\011,”í…ç°ÍÀ{\
! +ï£eµ4ǸûaÖä:\020\014¹Î%Kîñ¸a™z*‘dªž¸eb«£í$5\" M‰q3Êv›}Ÿo›½šM»ï<\
! àí³+²ÅäëíïÖ‹qñ}=ì\014¼ã§ã†EN>rOo”Ð>ût§‰×Â\030òK†¢ÐkË]×Æöoˆ7L Û\035ÜÍ\
! Q· ¢»ÝüfëÛ\026UØlŸlï\001Ó»%IOƱ.à\024ŸˆÂ[»gMP\036„\012wiÙwÛî‡@Ã~½uÚ·÷h²èJÙ\
! \006ÔšÖ¬cÞöBô6«Í¨Ï±U\017·S'ã¾\010êɦ\000ªÉÞþ™ÑHx6ùH¶Çj'XmKpÆêß\033‹í4|{\006¼\
! xR³èo\020°·QúÑ\036CÄ´Åî\\·ZŠð^VØ\012}|rº¡“«\006µØ\014ia\001=yúl\003 ÷ôÖ&:Ï¢O\033´V\
! Û—7ñ9›‚7)d\033ql»õ&ãá×ß|»ažWF“ðaÆÛà–ÓËH7¯\033\026ºæ@w^wêÊu†àA†àMÌ¡\
! àŸSçÕ\034=Å\010ëH\036uŠ9\006ÄŸôy°Ï\004òú³a8+×\007©§@C-h»ËÜúM”ÆÐû@¦Ôµ«tCƒ¬ðú,\
! ¼ûŒYØ!ONŸí?\015Šš?aIÛG¯÷/阴ùƒ±™}ÚÝzÈi\001¢>˜¬^3½ïæ9×PGˆA¶\013Õ-àDó\033\
! V¤wð1ÂW\025ô³ÏÝ$Õ’\001>em3š—MºXWªp^/J/¼Â²\002+\033\036\023Ç'Ä<Õ¯¡\034˜÷ôàû_è\000\036½¾\
! K¿,óð¬sôÂÎÒÐ×vK6=ñºw?h»#Æw3ƒÕaEžNz626ˆÍVp\015]úà ^Ó›ã€côÚ\007€m\014æ\
! 7òôŽnõ“w\030LÅØþ\011Í3Þ\026\022^é׋ó™Ä¸·E»[\006ïš‚öxê£m\017<Þ5ˆ«Q'»\031µÁ4hýý \
! Ê*¯ßd×ú\013\003|Þ–u_È„:ÓÎúU\037zÎÏx\\¾3e>]À3jvØÿç\010ö1VÔ·ú\035@Û`§ì}¯S”ã|ì\
! &s“3›ÞÒ\014£@s‚\016ËÓž[ŠtbWç¯\036x“úÝ]\022ϳ\027æHúeOÔË—™þv|A\031¿VÚ¼[A4;I\003ˆ\
! D¸ý.çŽGcÚ¨w4· è¥&æt{72ѽ\030Å{Y.\022êz\030é\003\001£L_j\004+;øxUÿá\007zäúù‹7¯œgúÏ\
! \\ôœ±¸þàŽ‚\020\027ôõ¸\021Æ\\¿ÔÒí‘Zzn¿Â¦‚…\016üßà/óW4º/ ½~Ùœg÷©3>—sòL'9x¿q\024\
! -“ùò\006ÂÅ›¥ÌZG_Ú“é.”8ƒÕ\016ê]ŸÍMm\033\001ô¼ŠÚ¾¼÷¤Ã£lr@G÷è\020¼Õ=SâîÁ\027>~è]\
! «éCÖjS\027þ\037±p¦\022~Œõ£=§4ÝQ¼ˆ}\026Ö]~“¼:\013C_õY§;î\022ØG\024ºDÃ`“ªR·pÉïÝèu\
! \0328þ¿J\027ÈÖI[¶œ¥Õø˜µ\026Z¿\036G\020/ÏzH3`lÕÌ?~:n\001Ñ|©M\037\003Õ-,\007´Þ‡Öbî&G›¶\025\
! Iÿ\"ÜqxÌ‘•žggi\014¶¾\025nÕÊé`\022ºÅÂ6UŒ¡½Ø¨bt›ÿzÒ&[â>ó\035}\026Ž›kk\024¡²ŸNm\
! S‰ãñxüÇjň|k7*â¿Á@\001Ÿ\005âÔ“XìnÌ™Ìæ\011Mfâ\002Ó`sXSÞ)¯®OX¸ýû°6zÂw`aÙ\016/\
! Ôᓾ\032ª™äJZ;†Ú»\036¿\033ë\017ü \036Âà™z(؈¿ÁÖìD2Çjd³´(=³WyÃg6v ß{\016ö¹†/\032Ç\
! «\0302ýM®OAZÝg*p{KÆ›\021‡§>\003ñWÎ\034ŸŒüCcùbQ•aºJúvTøøUë\011îƒÔ×(x\025ߧi8]×\
! 0JOé\036+†ÏõþLšâÑf»5ƒo\036î6hëÜÞxŒñøQ_çus»¦÷/εœ¿Ê\013J\033þ›?\010ì˜2ƹõjt\
! nwò\031_LÆüFz¶ñÏÈI÷Ì°sb\026Óë\022’\000<’™€\022\005ât,ŽŸœŸœž`kà‰FdØ=Ìéë7a®V«¡\
! -\015Ó|>bDì÷vûU‡-æÐ Ÿ3\007sêõ\035EÃëÙ÷šõõ`~Êߥhý\021$Mð\017û¡±vír‡€¦«n‘ã\
! Ê(åAè/¼]åz*ééQú̌قJWÝPSÝÇ7\000=†%<\027f°oÿ …ÎøÝw®[‹\002Š’†kþã‰ü\007h¿ü\
! â\001Žè\013b")
### end
--- 6,156 ----
import zlib
! data = zlib.decompress("xÚÝ=ksÛF’ßS•ÿ0Á–\015©–\")Év\022=¸kËNì;;ñÙÊ¥¶R)Õ\020\030’X\000‚‡(nnÿûõcf0xð!\
! ;»WuNJ\"\001LOwO¿§\007ºøêåW×{ÿJ¼¾~÷V¼ÿéÅÛ7WÂ;\032~>½\032^^¿ä\033O†ãcqˤ\
! ˆÊ(Md<\032½úÁ›|ùÅÅ¢\\Æô[É\020—Q\031+\021…—\036}ò&\0373¹|!ת\020?\025*\027o’Rå3\031¨‹\021=€c–ª\
! ”bQ–Ù‘úŠî.½+\031,”'‚\024\036NÊK/I\002¾4ê}þÕ}\026åªpF\\/ª\000œÿC&âøÛ¯Çb<>£ÿÅ÷\
! ï®7¡i®\000FžÆ=Ó\017IJ*Ê£\\ÝÉ8\012e\011\027àVQ¦¹Á¬(×DÒ4\015×âw1\003\010gâÛñ#!óHÆ\003Q¬¢\
! ¢\030ˆ…ŠïT\031\005ò\\,e>’31\026ÿüò‹RNy{ŒÃ‡gi¾„g\033\000¾üBžÅQr\013׃4Nó3ñ§1ü\013è¦<\
»‹`ýT¸áî\"½ƒ\005ªï=\033?\031Ïfxo¨Y!¤Fîh¥¢ù\002pœ¦qHOÜEjE3w\036I\000O\0313véL\022šd*ƒ\
! ÛyžVI\0103\005c\0053‹L†a”Ì/ŸÚG±š\001Œã§ç_~!ì¿iš‡*?*Ó\014ne÷¢Ha=Ä4\006˜}ÏMÓ²L\
! —G\011o”[Bˆp.¢(xîôôQƒìšËÇOéÆR\025…œ+^¯\022\031ÚÂWÝ×$äÌ\007¸Fc\013\025 \016™µf\034]äþô\
! Í\030ÿ;\027«(,\027 \007O\037¹\003\021c\000Ûæál6“j|ÞƒˆË‘œ1“÷\015ê_y–\026y==€\034g÷zÑAa@:˜Ó…è\
! •Ð1É`çÉ~Õ\017×\"·\005¢óÐ6`HG™G™ºI\023^Ð\006sU€ÿé\007«è5\030½×}‚s2~tn¡CÐÏûÙv12\
! ¶âb¤m'|$&¢ñÄ\017\036]CÆ_„Ñ\010bY\024—>k?ÑwŠ\014,\034Ž\000Û\000\006Í›\\D˹(òàÒç+Ãy4ó\005\030¬\
! yréËi±ŒÂ0Vþhò8™\026Ù9à\001\020\032ÐôD¼\012f¢ædYž†UPþ —\015\013ÿ³šº\006ž Ÿ9ã%cš.\025.‰'\026\
! ¹šÁDðÝŸ¼†Ÿ\027#Ù?\035èZBs=ž—碊†èvš¨7¿\000Ø£È9\004±”QrÅêìi\012=ÞÌç‹Å“É\033´ü\
! H\030È3,Ë“z\001²Éõ\"*Ä,ŠÁè_\004i¨&\026\013ú6\020¡šE\0110¡\\(\021§éí‘L£™R1ƒHgt§B'\030\031\036™«\
! 5\013?ª\034¬ðPˆk¸|\007æ?\012\006E ƒ\005Ž —ÌÓ…à\023r\005nB\011u_æ2@\001‡‰Á`%QVÅ’¾—\014!¯’2Z‚\
! ÁJE¸\006~‚#‰ãµàÅT„\010.\027²l¢Åã\0317Qà¯BT™8‚§4W\004L\015\032\013ÿƒ\011ˆUY*ƒ)\015Òe–&Àí\002\
! hc\006¾_¿Sqø\026tRóPD̽%àƒv\025Ñ\000äî¢Ð``P\004Ó”Nÿ\016–\007\010Í24‡1à™ó˜¥¼\005ü€6F/K‹\
! \"\002c;d\030¯À•33-/iT¢TX o¦ªÁ½\005\022•h9 <£Ð \\Êù€Ø=U@ N*Þ¯Ë\005Ø\020\035ÀÕ\\\037^Œ²\
! Ž‚èY–kzÞ”\"Láé$…¹0’\001Y(1T\003ôq\010¢\007#\022…k\007â\007ß\022b$ v„˜1\
! \030+“mDŸ\023\026ê^.³˜X¶HWŒÄ*Ío)*\031#~*ŒÊiz\017ž«\\0\000¸Øf\032€àK\032&\014з´¸ˆ’¬*Im\
! ëGÏl#µs×KÚáè&Øw\\zä™®ÆÞ\004þ]Œð\016(\007(P^:„í9\016”¸\024i¦’\003O\
! \033-o ¼¼ô\016ÁáÊðàpOHa\032\000 \032!\032T°«*?@ û‚Ér5\0046¬W×,üäq\\ž÷/¿]âm‚0B\007q1\
! Š&û“ä`1$h@£wµÉ\\…Þÿ!I\006…6I#³ŽŸÓºú1%ÉÖ6\010uŠ\014Z\025AÐaE¾å‚й\026dB\022_;\012\
! 4…8p&!þ—Ó\024PÏdQ¢™Å()™£„ð\0274 Òœ'À»\004¿6õ\014m–§KQ\004¹,ÁðFŒ\0268çIØõ•hG\
! \002NÀbÀ\001\024M‡š®ù‡Û\000šç!·s$\002ä\023NŽÀHÿŠ…Rå 6‚p\017ÈMµ\037F¬Á[$ó\012\011\007óMª\010–;Q\
! «Ú/´˜tDu¡,6È\011ô½`Z×BÉbÝ6ž/T\014Ö\022\037awÔ\011\000jfÁ\023)à¦\023\037Œò^—SØ\0310dÉ)ë\
! ÌL@ãF¦Þ„\002¸ð\005ZW\007''`Ô·=#Mœ’èðÏMS|\021¨8†‘\001fg~m³Ê|r\001Ѳ\001\007w½Öx}ÙGíÁ©\
! /ý\023ˆõhjA†¿\014A‹\000N\007¢\037 \\Á¼>j\004…¯ðù-ø\030(ûœ)\001è±\033®²úA4l€ù\034\027\027¥,«‚ãb=\
! ðÉØ\0077†\021:}6Á±\023‹\"žÎWM4PÐ\012*\033x9#ÈÝ£“\024\016ãA\013Þ¥EÙ\037\033\026&â\003é©á€ò\0270\035{qT\005#\
! “:d¬Ç€67ŸvD—SÀ{xwÒ\033Ó¢\011w)¸º°Êbð\024Oè iXC‚\010¡¨‚Å€õÓ\00416(akRG$C\
! c¼ÌÒÃ\017”2Í-—LóQ;´ß)ñÈ\000Èz2²iFêm\012€wðÆçËûn\021·\023.4ju„ÿ\032®n\022û\007\010ºÈÓ\025Ï\
! vºÐ#.\015‘?9©Eþôd‘G@짶\013üÇ4Ï×(\022\034\035¢4Êd-ŠL\005Ñ,\012xäŒbZ…Yªƒw\015nØ‹H\
! ‹eMì´H8ø@6¼ÈÅh²/\004¤ÜÝ´šç\012Ã]T¾™Qd9…„\026âk\035e®!g¸C’ þ\003…\007`¨aV\002Èe6\
! ”Zbe-†4\005C~P:Œ°\021T\000¦\032\002Ô\000\024h`/Èâ¶/9ÃäÆ”\013E`T£€:|”I€æᇴTun·TË)\
! \026YÈ~€Úâ\030\007#tji\014©‹Â‡h\024(ð\012\014Ôo•*\025…¶)Q\016\036^\001dH%\006à±m²Äá?\001š\000ýz¬X¢àA\
! út«ò¨Ä¢ñj\016\035mN–)‰9ð›Y\015\011ÉG¬ Á\0012\002•cäɈá6ÇIò\022¼å7Íg:\005yƒ\002SÔ\034¨Ÿ\
! íš.\001\003\016!€\023w‘$\016]H.PxX#>\033\012 PÀÖ¹\032&ª\034aº}«òÑ_0ÒÏn@xž\035=>ñ\032J€C¾Ã\
! !‚\036§\020j]”j‰µŽ\001®w\001‰#ò®0\001\005\004V(\014à\026\034FR¤\004K4\000Û–C®\002iL\016d\000\013¦U±\026\014\000V|…TÓÃ\
! æ\000Œ»(¯ÐñÔf\020p…&^\002:8☦sxN×5!Ș¢ÞXS,F\015W¦\023>³\010Q‚µhö\016°ˆ˜^«\024“E\023G\
! Þ& Ðè¯0…\022&\022ÃE5Ÿ#šFâY.fÑ=\031\013\\X-±dp²ÅÀÜ\036FåÊ.}\015ŒdÀ¦ý\016V\003§À‚*dW\
! ¶ÖóI\000©Péꤞ9‚«E\032\005\023\016Ú\007‹4-Hd\025ªž]$b)˜‘\032ZU\030ñ‚\005Œ!RTB%\0152\\ìÁ\003k#\014\
! \013]2\034›xð#\032ÖUT(V,ðó\020F\026´,qt‹A3W2<=[\011\016\015°\020Ãá\020Ò¾ÍƵöÅN\021m»×¥\010è¾<‚H\003\
! ·‡î\034\037,ð†uÄ‹§-·xÃõ^oò~‹÷8„ýãâé¤åº\021”\035€\030Cxý³Öq˜\034Ø\016¦\0162dâ5>àX\\ä\
! +1ŠÖi\025Áשvýà°FV\010¿¢\025ñ±˜#!\026kZX ée\"áTà^\003\005\004£çPX¡Ti…+:\007%£ÔH†<'\
! ¦9¼6Ä}Œ™R¬Îpi‚1\007\023\021%F5\015r:ì\"œ£%ÖÔ®9ñ9\004FU=4uw\015šÀ#%âdlµ\032m½Âê\027>\
! Ê\034(”ÂùȨ\"$P‚\"MÈ#ƒHUqiò\015!4ËkM*$\025© ¥€t-™OÀ¨*IÈ„‚4[c›.ã¬Èþ(\\\
! \007'Cv °\"eáo\021Š\012:Ř4T™JB™P|ŒL¥\012\02613\000w›”Æ' 0Zô¡ø\033\\1V'¯Ð\022‚mž/8ö\031\
! ì#¨©¡ª\026Y&È'ÃXéUƒõb,uÑlV• #CŽçIÄQákËÉ\017£ (™ç,\036Æ&ƒ>: ÍÄ\033…›–ë\
! ›P®‹CÈ¿á\027£eÆSì¼\006û½$aT\0042\017ë%:\010S”m\
! šA`™\021©ÓÐ\017\007¡j¦rg X—;U\013nk¬ÀÚgL¾(?ĸÇÒ!\016@ò\0313Ö•#;ÅB.í\004\003£CG.\022\020÷Ö\
! \034òª‡º2\012ZQ€š€\001\004!a‡«¹\037Dy€{ÓÌL¸\000qFžfy„\005AˆÝ«er.\"¶ì`Y\027ÌE,ÂX³\\¯2\027\
! Z\012ÉåS\031°›11š‹\001ÏMà…N\025Ú:õ7\035ÛÔéš©\002`pÂ…d\020\015¸\027RÌÒXý\001D\000\020\026D3&‹ÒL\\L•ä\
! »ÎY;0Ä~É¡W\010ÑxAY]â\022HKn\003Î\0304•ì³ó\004Yvq çãz²\\ÑU\012\002õ`\037«bÄ!°˜¡\\âÈhÖ\000\024\
! p\021\021-\011\007+\030·ÂÓ\\¨YRF„F ”¥dÏ\005±€^íA“Óš\010ó´1Q›é?‚í®\035/¨K¡#'^Ì¢!\007ài¿\
! \035-6Ë\002ù„¨Èpß!´‚1pÖüB-'×(ë\027#ø„«Tâp\016¿a)\015~hE†\015ÅÆž\002Ø*ÃF\003\021•\0051`*Y§\
! !z‰‘Dâ?ÞhS×\016öáÃ\0325äò%nß`Q¬ÏÕà°\026>i\025‡\020¸H½MdÖŽÄ4¨ò\034±5\002ˆ8ˆfF\032\021\
! V\000é ‹¨…E¤U Š6ð\012ÉŽÓ@º\\šEq7‚ÕÑ$?‹bb\027‚Ã\037F˜\030ÁbËôp\
! ±\003ËÍÔB#)Ú\"³¬Š ¦\033j.d@±¡¨?ƒÐ\002¼Âì\004wË#Œé«²ÇÄK\012úŒ5«}O×~áHÖz‹¼l)\
! âº\025ô0û~\006Ø\010é¿*•¯5GÁ\026D\020@qÐ6˜¹\011ÎJ\0270\027d”3NÐIÝ-S¯±N‚1©vVÜøÃz]\012\
! U\\4¢ü)¼Ã\035\007B‰xò\033â5hæ“zF®‹Â\002…\030ü“SÊÕ\034‹ÛB±¢c*Ö—/·!ÑvG½½TË\"/,–\\h=\
! M‘Š“’,â²Ø&—‹F9²ñ&ç-†iÆ.pæÔ“ˆ£¢]ÓÅÂ\027yFŒ\007tÜPk\0352ÛÖlø9c“M\032âD»š\
! X'Ë\035\030\003BJf â>•©·h§áxŒ7/Å\001\027ê#ëbMÓm‡\031*(󃲡wHŒ·_Œ¶µ÷žQì“P²¨:Ñ\
! rcg\012\013ªõüT\"SÜ@R¯âœ•\\ÏÚ\\J;¬Å-I)é\000\025 a\0048Ë\0367¾µÂý\034‰9\002ƒ\024«ð\010¨¡ÝT¿Þ\033\
! hm4;»\012¶(_êºÂ¥ÇûMfq©ÊE\012ƒ¾uíî\034]|ut$Þñ¦\0027Ü {V˜Kb*žÎfØ{3År3–î\
! \006v•—àæ±SßÙ|À\035€ò¬½GL\035IØU{é\001.ñú¨LMGÒéØv!•òh©Ö*ÿk´¨æM\017“xºËÍÓý\
! é'ÏžfØž'§X³½¿ôžyý¢ÎtÓ è™é“À\026ŠÊ \0147Ë£4\007›çMºÝë\027iFöXOƇ\022<\010Dñ÷ň\
! ïî\036Wåsê\030þ‰~o\030\007)\035¡å\\>:jl5?´ñàaÍ\007“X]ˆÓ(i»ß`3<Þàþ\0164îŒ\007mx¢wyÝ.\
! 6^\022TÝ\033ÈSswÁQGœ\033Ÿ!iã¦H<”ÌlB?R¦N›`CÛ3M›½¬){ÏÛ˜:\014ŸÞP{o~&~Ñû›`\
! Ê—KÜ7ÁÕ_ÛD?y:n\022}úyDk‘x\020Ѹ¤`á!ŒÉe\006Š¶X\027X£0«¬\015!S¯¿ÜP\003\006‰%s\006ã=nv\
! ÜAß\023\017ÉÑ3~\006¡ÏË\022ËäoÓ9Å’Ÿ,Ï8ØP*\011f{Áù‰š‚¯\035£¶ÇR\031¡A¯®¼M86ú‹4\017iã•J\
! äg\001:ÛÜÛF\011Ïa‘C3áíįќ…\015¯ùò!¾“\023ÊF\013\"›?\"šn^ãwOŸI\"¥v ùn\016‡Á÷f×Tï‘\
! \021†ìm:½r¿~©Jð®E›_ï|JQÎ\032÷\017ÍžÖÅtÒ\032þ\036œwñ‘ša½ÉñññXw©]Œ¦“A\023$=O\031F\
! Ú\004ÄvÀBÑÁX²v€\015[ëY÷ÅÙö»€6Ý\011sH¸\021$å\036ÅY;sç—4ð£\016²½‰Kðg¢î\014¦ã ìš\
! ®LK\031ï7[ã4\017\035ÚÀªkáÖ¡¶Ìܘ5©–\030Á¶&\024¸…8Ø>ìuwÔbç Ÿ¨6Ö\032Æ\005³6‘×È\017î\034)Lb\
! Т\0041?k͘7á7Ǽî\016Y´G´Ùm6Ot•ö]š»Yêp8lÌj\036¶ª;HÀpé\023\020Ú¸?S÷¾n#r¶Æ\
! ¸mî4ö(\033ב¶µìš\023nxìÑ\037õª}£\030õ\026\017 9uqìþ4I˜\001¤{ol‚Ôâê> ¹\027̶\007©–péuÑ\
! ôûØjS“ »\\ådØY™\015ÆoÔ\007PÔÒk÷Q\0327sêƒ \020ëV2OÈF5ñû™/Ÿ™–¦(\001“\006\031\011_¶É(†\
! 5_q{²¾Õjj¦~+È\032ÝSi\014\013»\032t\011×mç'åçZ\032õ\0307A%Ø\002]@@Õ`Ìt\023W¬ƒÛéË8k½vzš¸Í\
! ŸxTß4lºn$ÝìT¨¾Yt\033\012\"ì|U…ÆØH\032—*ÝÊ”Âv\010Ü£jdê4µðH+Iµð0b¾i²2ÏÓÞ§Ý\
! ö1\033?ûs\001™Ö²€\037 ®ZW\025mÜŠ “ŠÚä™*‰h¥5ÁúÔ ¥\032ë|\013¥K¢nã†.³ÎLO\014\001\037Øã‚\
! ¦’êS¥\024{»ât\005Àü—ØÈâ\013ê_)ܵà¡(³\003ÝCgŠ60\015u²ØšcÝσô\036¶\030\011‚\016¿Á\030ÃO\024\002[`\
! ¡®\022¤¤>Q£͞¤ik5ôºÑžöÌË]\022\006e;A'Ŭl\035`âa›u[Û¸¼ÅjÊ\005ú8<î\004ëŠ\014‚Ò5\
! :lb+¸ùûb\012[äâÑKÉ\007ŽÌ®×aó8Œ[\0051Riê\037þ÷¯®\035Ûï†þ\"\012C\005kG!¸OY>5Ø›\034Îú\
! c×îo\001€ŠòxþÔ;œcT\035’½F\010j¾÷&ê&*m`À‘·Á`ž:ø¿ e÷û\016Üã?ÝÚ\016%d\002aT ~\
! \000âØ9ÉÝ\010…Ù˜ïƒEÎ\033Ä»0ÐûÈŸ?\037r}?¢iÏþ\004oò´íã\014݃'6·©wmÛ‚<Ê\014YèxF—w\
! ’¯ú“Ñèâ«_®^>¿~þ‹>òV%¬»iÂVó\000GÒ{<Ê`q꣒¿×øD3lÂ\013*,Þ\015y\023àñcѼ2ŒU\
! 2/\027br)N\016ë¡¿7¹HZvÙ\032úËñ¯çÇÄA\004\017ŽÏE$.h˜ž\000.üùχÍÇï.\025uhà+\000\000\010Žþ%ú\
! uˆ+=¤\033\007þ™Øó\032\011$ÔŽ4\024]^ŠS¤×Þ\000|ñ\"íÅ=~Ü/&fN}hóÒrwX¦oӕʯÀQ\036\034\036vGÿ\
! ¾\035 ™g°÷0^©\036\032þÙ¼ä|Õ\037õ¯Ñè×_ëCL$,{ZÃ÷?~¼ö'í|Ù7EB.\016¸o!Ù×@Ù\"5Ïø±\
! š\032·¾áP\014„€\023[êoö\001à‹ƒÎ¶\004VMP&<ð¹f*u\035n)CÏB¹èbÛ‹\012_;H›üȼdÈë7HTü¢\
! M\021^´ú\010K\0179êÌÉ\0311õå÷-ˆ<\015n!‰þæèð\017±Fš&k‹@Œe9\020ÇÇâ£ÊÄÉx|*Ž¿=\033Ÿž\035\037÷\034\
! \004ÕÅ\026.9û££\000<—a”š8д•œ‘ÿøÏWë\017\003\021=}PÁT¬}ý݆€ÝŸÿ\012$ÐÜxuTJßÿ½(`\001\
! Ï\015Œáû¿\027\001*::\010àw4ÙyZ˜Í›×\026˜ñp<~Ô3D;\035{\023<\006Q£@ö;7<“\021ÿŸÎ{¦¸‹¹;Æ\
! ´jË-e2ÚÆerûAoȸ9ÇŽ¯]HT@Ø\003Ò\036JÖS–ß’r颇^4þ¶aÕz+b´È9‹¿³NTeq*Ã\
! n\005\0009Â÷<\033ÿ\022‡\032â׈„…‚|Šè[Vq\031á\021XÂê\010›§ŒgÐýh\014ºî°qœS~ƒ-…Þdà¶\026j’x°ñ\
! \002\"ž\021¯\033~ògÍNळ\032?šN8,ά\026iìôÕw›ý¨¾tæ¾äÀn±òLäòxÏÔ?å¿K\037_6älºÃë\
! \027Ì°\004Ü°Æö\011\005uþ4…Ât\020ú›á-\036\010îõVhµFï\006iºújpF\"mmñ\000KzáIÆæøÎ':¸€yî4ÅÓ\
! TæP\007··ÕG ŽœÂ{”¸µÅúe\032\"Wx t_èu¸wÅ\024ûå¨9Ïô±¹ª`oÖÚ€—¨iN3ij\027šï®è$\
! Zh6ôþÈÓ§š{)v\011\020˜g8\015Ÿkvê\033>oqû§ã¦Áhm\034õ™\036#\011ý}ƒÎ*Ö¶§Þ’Ô½ƒ=‰~‹G\
! FZ\026Q`¨!\026Ý 6~ÓßéŒ\036Œ)ðE×Ói´\011 \017ZL¥Vðþ¦Žb×`¸”÷7ætl]˜2Ÿ|¯Û¥dÈ#à7\
! ±œªØ›¼À/BËE_(ºÑÔÓ*êžÉ>Ú5)æ‘&3º”?œÖm4šY\015™?ëïŸLi®æê~\013™tÿßK#Mi\010\
! ü \033V_Ù†ÕO&\025\034>ÆK,Lljï\\²\0247®1Ý$ØàÔüÍH;£\014êoè’ÀBÙÞØZx\016Û\014¼wò>ZVKs\
! v¼\037fM®\003Áë\\²ä\036\033–©§\022I¦ê‰[&¶Ú8ÚNR#\002Ú”\030÷wÀl·ùØlú®Ù Ú´ûÎ\003Þ>»\"[L\
! ¾Þso½p\030_\022ÄÎÀ;~:nXÔéä#7\022G\011mîOwšx-Œ!¿Ù(\012½¶Üumlÿ.|Ã\004º-ÉÝ\034u\013\"ºÅÎ\
! oöÛmQ…ÍöÉÖù\0360½[’ôd\034ë\002Nñ‰(¼³\033å\004åA¨pk˜}gð~\0104ì×;§g|ÎŽ®”m@iÍ:æ\
! m/D\037h³ÚŒú\034[õp;u2î‹ žl\012 šìíŸ\031„g“d{¬vÒŠÕ¶\004g¬þ½±ØN÷gÀ‹ÇC‹þ\006\001\
! {Û\030¥\037ìÙGL[ìÎu«\011ï\005i…ý×Ç'§\033ÚÇjP‹Í\026\026Г§Ï6\000zO¯Š¢C4úˆCû<·íDáÃ=\005\
! !oRÈ6âØëëMÆï¿ùvÄ=¯â&áÃŒ·Á-§’n^7:-tÍî¼îÔ•ë\014Áƒ\014Á›˜“È?§Îû@z\
! Š\021Ö‘<ê\024s\014ˆ?éChŸ\011äõgÃpV®\017RO†úÞv—¹õ‹))¡—L©íkWé†\006YáõYx÷\031³°Cž\
! œ>Û\032\0245Â’¶^ï_Ò1ió\007c3û´»õÓ\002D}0Y½:gzßÍs®¡Ž\020ƒlë«[À‰æ7¬H?ÂÇ\010ß\
! Ð\007Î>w“TK\006ø”µÍh^6éb]©Ây\033*½e\013Ë\012¬lx6\035Ÿ\020óT¿5s`^\016„/¡SôÎ0ýnÏóÎ\
! y\017;KC_Û}àôÄëÞý íŽ\030_%\015V‡\025y:éÙÈØ 6[Á5té\017ƒzM¯«\003ŽÑ»~\036\000¶1˜_\003Ô;ºÕÄ\
! Þa0\025cû'4Ïx[Hx¥ß†Î\007!7âÞ\026ín\031¼k\012Úã©y·=ðx× ®þµFìfÔ\006Ó õ÷ƒ*«¼~}^ë\
! /7ð!_Ö}!\023êL;ëW5~è9?ãqùΔùt\001ϨÙaÿŸyØÇXQ³ìw\000mƒ²÷½NQŽó±›ÌMÎ^ÕPƒ\036¹~þâí+ç™þƒ\036=\007;®?¸£\
! Ä\005}=n„1×/µt{¤–žÛ¯°©`¡\003ÿ·øËüu’î[o¯_6çÙ}Ô\017\003<ÓI\016ÞoœËd¾¼pñf\
! )³Öy›ödº\013%Î`µƒz×gsSÛF\000=oζo\014>éð(›\034ÐyAꩶºgJÜ=øÂÇ\017½«u5}ÈZmjýÿ\
! #\026ÎT±~´ç”¦;Š\027±ÏºËo’Wgáoè«>`uÇ]\002ûˆB—h\030lRUê6\020.ù½\033½N\003ÇÿWé\002Ù:\
! iË–³´\032\037\022³ÖBëwò\010âåY\017i\006ŒšùÇOÇ- š/µéc º…å€ÖûÐZÌÝähÓö¯\"é_„»³1\016\
! 9²Òóì,ÁÖ·ÂZ9½\023LB·XئŠ1´\027\033UŒnó_¥ÚdKÜg¾£ÏÂqsm\"TöÓ©m*q<\036ÿX\
! \030‘oíFEü'#(à³@œz\022‹Ý9\010Ú<\026ÊL\\`\032lNˆÊ;åÕõ\011\013·\037ÖFOøâ-,Ûá…:|ÒWC5“\\\
! IkÇP;p×ãwcý\037ÄC\030×ðEãL\027C¦¿uö)H\
! «ûLå\021noÉx3âðÔg þʙ㓑h,_,ª2LWIߎ\012\037¿j=Á}ú\032\005¯âû4\015§ëúï[é)ݳÌð\
! ¹ÞŸISã‹É˜ß\
! HÏ6þy>é\036TvŽébz]B\022€ç@\023P¢@œŽÅñ“³ñ“³Ó\023l\015<ш\014»'H}ýúÍÕj5´\005²ašÏGŒˆ\
! ýÞn¿ê°ÅœTäÃí`N½¾\003«hx=û2µ¾\036ÌOùc\030¿ù•¤\011þÁD4Ö®]î\020ÐtÕ-r\\\031¥<\010ý…·«\
! \\O%==JŸ™1[P骻\023jªûøÚ¡Ç°„çÂ\014öí_ÁÐ\031¿û¢wkQ@QÒpÍ”’ÿ°ï—_ü/ÿ_z¾")
### end
From anadelonbrin at users.sourceforge.net Wed Dec 22 01:22:29 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 01:22:32 2004
Subject: [Spambayes-checkins] spambayes/spambayes/test test_stats.py, 1.1,
1.2
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31740/spambayes/test
Modified Files:
test_stats.py
Log Message:
Update tests to reflect new Stats() constructor.
Add checks for the cost calculations.
Check that getting stats from a certain date works correctly.
Index: test_stats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_stats.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** test_stats.py 21 Dec 2004 21:30:13 -0000 1.1
--- test_stats.py 22 Dec 2004 00:22:26 -0000 1.2
***************
*** 15,27 ****
class StatsTest(unittest.TestCase):
def setUp(self):
- self.s_cut = options["Categorization", "spam_cutoff"]
- self.h_cut = options["Categorization", "ham_cutoff"]
- self.h_string = options["Headers", "header_ham_string"]
- self.u_string = options["Headers", "header_unsure_string"]
- self.s_string = options["Headers", "header_spam_string"]
self.messageinfo_db_name = "__unittest.pik"
self.messageinfo_db = MessageInfoPickle(self.messageinfo_db_name)
! self.s = Stats(self.s_cut, self.h_cut, self.messageinfo_db,
! self.h_string, self.u_string, self.s_string)
def tearDown(self):
--- 15,21 ----
class StatsTest(unittest.TestCase):
def setUp(self):
self.messageinfo_db_name = "__unittest.pik"
self.messageinfo_db = MessageInfoPickle(self.messageinfo_db_name)
! self.s = Stats(options, self.messageinfo_db)
def tearDown(self):
***************
*** 43,48 ****
self.messageinfo_db.close()
self.messageinfo_db = MessageInfoPickle(self.messageinfo_db_name)
! self.s = Stats(self.s_cut, self.h_cut, self.messageinfo_db,
! self.h_string, self.u_string, self.s_string)
self.assertEqual(now, self.s.from_date)
--- 37,41 ----
self.messageinfo_db.close()
self.messageinfo_db = MessageInfoPickle(self.messageinfo_db_name)
! self.s = Stats(options, self.messageinfo_db)
self.assertEqual(now, self.s.from_date)
***************
*** 235,238 ****
--- 228,241 ----
data["num_unsure_trained_spam"]) /
data["total_spam"])
+ self.assertEqual(new_data["total_cost"],
+ data["num_trained_ham_fp"] *
+ options["TestDriver", "best_cutoff_fp_weight"] + \
+ data["num_trained_spam_fn"] *
+ options["TestDriver", "best_cutoff_fn_weight"] + \
+ data["num_unsure"] *
+ options["TestDriver", "best_cutoff_unsure_weight"])
+ self.assertEqual(new_data["cost_savings"], data["num_spam"] *
+ options["TestDriver", "best_cutoff_fn_weight"] -
+ data["total_cost"])
def test_AddPercentStrings(self):
***************
*** 293,297 ****
self.assertEqual(s[9], "Manually classified as spam:\t0")
self.assertEqual(s[10], "")
! if self.h_cut <= score < self.s_cut:
self.assertEqual(s[11], "Unsures trained as good:\t0 (0.0% of unsures)")
self.assertEqual(s[12], "Unsures trained as spam:\t0 (0.0% of unsures)")
--- 296,301 ----
self.assertEqual(s[9], "Manually classified as spam:\t0")
self.assertEqual(s[10], "")
! if options["Categorization", "ham_cutoff"] <= score < \
! options["Categorization", "spam_cutoff"]:
self.assertEqual(s[11], "Unsures trained as good:\t0 (0.0% of unsures)")
self.assertEqual(s[12], "Unsures trained as spam:\t0 (0.0% of unsures)")
***************
*** 372,376 ****
self.assertEqual(s[17], "Spam correctly identified:\t33.3% (+ 33.3% unsure)")
self.assertEqual(s[18], "Good incorrectly identified:\t33.3% (+ 33.3% unsure)")
! self.assertEqual(len(s), 19)
def test_get_all_stats(self):
--- 376,383 ----
self.assertEqual(s[17], "Spam correctly identified:\t33.3% (+ 33.3% unsure)")
self.assertEqual(s[18], "Good incorrectly identified:\t33.3% (+ 33.3% unsure)")
! self.assertEqual(s[19], "")
! self.assertEqual(s[20], "Total cost of spam:\t$11.60")
! self.assertEqual(s[21], "SpamBayes savings:\t$-9.60")
! self.assertEqual(len(s), 22)
def test_get_all_stats(self):
***************
*** 395,401 ****
self.assertEqual(s[17], "Spam correctly identified:\t40.0% (+ 20.0% unsure)")
self.assertEqual(s[18], "Good incorrectly identified:\t33.3% (+ 16.7% unsure)")
! self.assertEqual(len(s), 19)
def _stuff_with_data(self, use_html=False):
# Record some session data.
self.s.RecordClassification(0.0)
--- 402,417 ----
self.assertEqual(s[17], "Spam correctly identified:\t40.0% (+ 20.0% unsure)")
self.assertEqual(s[18], "Good incorrectly identified:\t33.3% (+ 16.7% unsure)")
! self.assertEqual(s[19], "")
! self.assertEqual(s[20], "Total cost of spam:\t$23.40")
! self.assertEqual(s[21], "SpamBayes savings:\t$-19.40")
! self.assertEqual(len(s), 22)
def _stuff_with_data(self, use_html=False):
+ self._stuff_with_session_data()
+ self._stuff_with_persistent_data()
+ self.s.CalculatePersistentStats()
+ return self.s.GetStats(use_html=use_html)
+
+ def _stuff_with_session_data(self):
# Record some session data.
self.s.RecordClassification(0.0)
***************
*** 411,414 ****
--- 427,431 ----
self.s.RecordTraining(False, 1.0)
+ def _stuff_with_persistent_data(self):
# Put data into the totals.
msg = Message('0', self.messageinfo_db)
***************
*** 436,441 ****
msg = Message('8', self.messageinfo_db)
msg.RememberClassification(options['Headers','header_unsure_string'])
- self.s.CalculatePersistentStats()
- return self.s.GetStats(use_html=use_html)
def test_with_html(self):
--- 453,456 ----
***************
*** 449,452 ****
--- 464,504 ----
self.assert_(' ' not in line)
+ def test_from_date_empty(self):
+ # Put persistent data in, but no session data.
+ self._stuff_with_persistent_data()
+ # Wait for a bit to make sure the time is later.
+ time.sleep(0.1)
+ # Set the date to now.
+ self.s.ResetTotal(permanently=True)
+ # Recalculate.
+ self.s.CalculatePersistentStats()
+ # Check.
+ self.assertEqual(self.s.GetStats(), ["Messages classified: 0"])
+
+ def test_from_specified_date(self):
+ # Put persistent data in, but no session data.
+ self._stuff_with_persistent_data()
+ # Wait for a bit to make sure the time is later.
+ time.sleep(0.1)
+ # Set the date to now.
+ self.s.from_date = time.time()
+ # Wait for a bit to make sure the time is later.
+ time.sleep(0.1)
+ # Put more data in.
+ msg = Message('0', self.messageinfo_db)
+ msg.RememberTrained(True)
+ msg.RememberClassification(options['Headers','header_spam_string'])
+ msg = Message('7', self.messageinfo_db)
+ msg.RememberTrained(False)
+ msg.RememberClassification(options['Headers','header_spam_string'])
+ msg = Message('2', self.messageinfo_db)
+ msg.RememberTrained(True)
+ msg.RememberClassification(options['Headers','header_ham_string'])
+ # Recalculate.
+ self.s.CalculatePersistentStats()
+ # Check that there are the right number of messages (assume that
+ # the rest is right - if not it should be caught by other tests).
+ self.assertEqual(self.s.GetStats()[0], "Messages classified: 3")
+
def suite():
From anadelonbrin at users.sourceforge.net Wed Dec 22 01:24:01 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 01:24:04 2004
Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py, 1.40,
1.41 ProxyUI.py, 1.55, 1.56 UserInterface.py, 1.50, 1.51
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32095/spambayes
Modified Files:
ImapUI.py ProxyUI.py UserInterface.py
Log Message:
Use new stats manager properly.
Index: ImapUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v
retrieving revision 1.40
retrieving revision 1.41
diff -C2 -d -r1.40 -r1.41
*** ImapUI.py 21 Dec 2004 23:08:51 -0000 1.40
--- ImapUI.py 22 Dec 2004 00:23:58 -0000 1.41
***************
*** 111,115 ****
"""Serves the HTML user interface for the proxies."""
def __init__(self, cls, imap, pwd, imap_session_class,
! lang_manager=None):
global parm_map
# Only offer SSL if it is available
--- 111,115 ----
"""Serves the HTML user interface for the proxies."""
def __init__(self, cls, imap, pwd, imap_session_class,
! lang_manager=None, stats=None):
global parm_map
# Only offer SSL if it is available
***************
*** 123,127 ****
del IMAP4_SSL
UserInterface.UserInterface.__init__(self, cls, parm_map, adv_map,
! lang_manager)
self.classifier = cls
self.imap = imap
--- 123,127 ----
del IMAP4_SSL
UserInterface.UserInterface.__init__(self, cls, parm_map, adv_map,
! lang_manager, stats)
self.classifier = cls
self.imap = imap
Index: ProxyUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v
retrieving revision 1.55
retrieving revision 1.56
diff -C2 -d -r1.55 -r1.56
*** ProxyUI.py 21 Dec 2004 23:16:28 -0000 1.55
--- ProxyUI.py 22 Dec 2004 00:23:58 -0000 1.56
***************
*** 165,169 ****
UserInterface.UserInterface.__init__(self, proxy_state.bayes,
parm_ini_map, adv_map,
! proxy_state.lang_manager)
state = proxy_state
self.state_recreator = state_recreator # ugly
--- 165,170 ----
UserInterface.UserInterface.__init__(self, proxy_state.bayes,
parm_ini_map, adv_map,
! proxy_state.lang_manager,
! proxy_state.stats)
state = proxy_state
self.state_recreator = state_recreator # ugly
Index: UserInterface.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v
retrieving revision 1.50
retrieving revision 1.51
diff -C2 -d -r1.50 -r1.51
*** UserInterface.py 21 Dec 2004 23:08:51 -0000 1.50
--- UserInterface.py 22 Dec 2004 00:23:58 -0000 1.51
***************
*** 267,271 ****
def __init__(self, bayes, config_parms=(), adv_parms=(),
! lang_manager=None):
"""Load up the necessary resources: ui.html and helmet.gif."""
BaseUserInterface.__init__(self, lang_manager)
--- 267,271 ----
def __init__(self, bayes, config_parms=(), adv_parms=(),
! lang_manager=None, stats=None):
"""Load up the necessary resources: ui.html and helmet.gif."""
BaseUserInterface.__init__(self, lang_manager)
***************
*** 273,276 ****
--- 273,277 ----
self.parm_ini_map = config_parms
self.advanced_options_map = adv_parms
+ self.stats = stats
self.app_for_version = None # subclasses must fill this in
***************
*** 921,933 ****
def onStats(self):
"""Provide statistics about previous SpamBayes activity."""
- # Caching this information somewhere would be a good idea,
- # rather than regenerating it every time. If people complain
- # about it being too slow, then do this!
- # XXX The Stats object should be generated once, when we start up,
- # XXX and then just called, here.
- s = Stats.Stats()
self._writePreamble("Statistics")
! stats = s.GetStats(use_html=True)
! stats = self._buildBox("Statistics", None, "
".join(stats))
self.write(stats)
self._writePostamble(help_topic="stats")
--- 922,933 ----
def onStats(self):
"""Provide statistics about previous SpamBayes activity."""
self._writePreamble("Statistics")
! if self.stats:
! stats = self.stats.GetStats(use_html=True)
! stats = self._buildBox("Statistics", None,
! "
".join(stats))
! else:
! stats = self._buildBox("Statistics", None,
! "Statistics not available")
self.write(stats)
self._writePostamble(help_topic="stats")
From anadelonbrin at users.sourceforge.net Wed Dec 22 01:25:48 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 01:25:50 2004
Subject: [Spambayes-checkins] spambayes/spambayes Stats.py,1.12,1.13
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32506/spambayes
Modified Files:
Stats.py
Log Message:
Simplify constructor to take just the messageinfo db and an optionsclass object.
We then have access to all the options we want, and (more importantly) the values
will be updated correctly if the user changes options in mid run.
Index: Stats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Stats.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** Stats.py 21 Dec 2004 23:19:41 -0000 1.12
--- Stats.py 22 Dec 2004 00:25:45 -0000 1.13
***************
*** 52,67 ****
class Stats(object):
! def __init__(self, spam_threshold, unsure_threshold, messageinfo_db,
! ham_string, unsure_string, spam_string, fp_cost, fn_cost,
! unsure_cost):
self.messageinfo_db = messageinfo_db
! self.spam_threshold = spam_threshold
! self.unsure_threshold = unsure_threshold
! self.ham_string = ham_string
! self.unsure_string = unsure_string
! self.spam_string = spam_string
! self.fp_cost = fp_cost
! self.fn_cost = fn_cost
! self.unsure_cost = unsure_cost
# Reset session stats.
self.Reset()
--- 52,58 ----
class Stats(object):
! def __init__(self, options, messageinfo_db):
self.messageinfo_db = messageinfo_db
! self.options = options
# Reset session stats.
self.Reset()
***************
*** 87,93 ****
def RecordClassification(self, score):
! if score >= self.spam_threshold:
self.num_spam += 1
! elif score >= self.unsure_threshold:
self.num_unsure += 1
else:
--- 78,84 ----
def RecordClassification(self, score):
! if score >= self.options["Categorization", "spam_cutoff"]:
self.num_spam += 1
! elif score >= self.options["Categorization", "ham_cutoff"]:
self.num_unsure += 1
else:
***************
*** 99,103 ****
# If we are recovering an item that is in the "spam" threshold,
# then record it as a "false positive"
! if old_score > self.spam_threshold:
self.num_trained_ham_fp += 1
else:
--- 90,94 ----
# If we are recovering an item that is in the "spam" threshold,
# then record it as a "false positive"
! if old_score > self.options["Categorization", "spam_cutoff"]:
self.num_trained_ham_fp += 1
else:
***************
*** 105,109 ****
# If we are deleting as Spam an item that was in our "good"
# range, then record it as a false negative.
! if old_score < self.unsure_threshold:
self.num_trained_spam_fn += 1
--- 96,100 ----
# If we are deleting as Spam an item that was in our "good"
# range, then record it as a false negative.
! if old_score < self.options["Categorization", "ham_cutoff"]:
self.num_trained_spam_fn += 1
***************
*** 128,132 ****
# Skip ones that are too old.
if self.from_date and m.date_modified and \
! m.date_modified > self.from_date:
continue
--- 119,123 ----
# Skip ones that are too old.
if self.from_date and m.date_modified and \
! m.date_modified < self.from_date:
continue
***************
*** 134,138 ****
trained = m.GetTrained()
! if classification == self.spam_string:
# Classified as spam.
totals["num_spam"] += 1
--- 125,130 ----
trained = m.GetTrained()
! if classification == self.options["Headers",
! "header_spam_string"]:
# Classified as spam.
totals["num_spam"] += 1
***************
*** 140,144 ****
# False positive (classified as spam, trained as ham)
totals["num_trained_ham_fp"] += 1
! elif classification == self.ham_string:
# Classified as ham.
totals["num_ham"] += 1
--- 132,137 ----
# False positive (classified as spam, trained as ham)
totals["num_trained_ham_fp"] += 1
! elif classification == self.options["Headers",
! "header_ham_string"]:
# Classified as ham.
totals["num_ham"] += 1
***************
*** 146,150 ****
# False negative (classified as ham, trained as spam)
totals["num_trained_spam_fn"] += 1
! elif classification == self.unsure_string:
# Classified as unsure.
totals["num_unsure"] += 1
--- 139,144 ----
# False negative (classified as ham, trained as spam)
totals["num_trained_spam_fn"] += 1
! elif classification == self.options["Headers",
! "header_unsure_string"]:
# Classified as unsure.
totals["num_unsure"] += 1
***************
*** 234,243 ****
data["total_spam"]
! data["total_cost"] = data["num_trained_ham_fp"] * self.fp_cost + \
! data["num_trained_spam_fn"] * self.fn_cost + \
! data["num_unsure"] * self.unsure_cost
# If there was no filtering done, what would the cost have been?
# (Assuming that any spam in the inbox earns the cost of a fn)
! no_filter_cost = data["num_spam"] * self.fn_cost
data["cost_savings"] = no_filter_cost - data["total_cost"]
--- 228,241 ----
data["total_spam"]
! fp_cost = self.options["TestDriver", "best_cutoff_fp_weight"]
! fn_cost = self.options["TestDriver", "best_cutoff_fn_weight"]
! unsure_cost = self.options["TestDriver",
! "best_cutoff_unsure_weight"]
! data["total_cost"] = data["num_trained_ham_fp"] * fp_cost + \
! data["num_trained_spam_fn"] * fn_cost + \
! data["num_unsure"] * unsure_cost
# If there was no filtering done, what would the cost have been?
# (Assuming that any spam in the inbox earns the cost of a fn)
! no_filter_cost = data["num_spam"] * fn_cost
data["cost_savings"] = no_filter_cost - data["total_cost"]
From anadelonbrin at users.sourceforge.net Wed Dec 22 01:27:19 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 01:27:22 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_imapfilter.py, 1.48,
1.49 sb_pop3dnd.py, 1.14, 1.15 sb_server.py, 1.34, 1.35
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv507/scripts
Modified Files:
sb_imapfilter.py sb_pop3dnd.py sb_server.py
Log Message:
Use new stats manager properly.
Index: sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v
retrieving revision 1.48
retrieving revision 1.49
diff -C2 -d -r1.48 -r1.49
*** sb_imapfilter.py 20 Dec 2004 02:49:47 -0000 1.48
--- sb_imapfilter.py 22 Dec 2004 00:27:17 -0000 1.49
***************
*** 98,101 ****
--- 98,103 ----
import StringIO
+ from spambayes import Stats
+ from spambayes import message
from spambayes.Options import options, get_pathname_option
from spambayes import tokenizer, storage, message, Dibbler
***************
*** 1028,1031 ****
--- 1030,1034 ----
classifier = storage.open_storage(bdbname, useDBM)
+ message_db = message.open_storage(*message.database_type())
if options["globals", "verbose"]:
***************
*** 1083,1089 ****
else:
imap = IMAPSession(server, port, imapDebug, doExpunge)
httpServer = UserInterfaceServer(options["html_ui", "port"])
httpServer.register(IMAPUserInterface(classifier, imap, pwd,
! IMAPSession))
launchBrowser=launchUI or options["html_ui", "launch_browser"]
if sleepTime:
--- 1086,1096 ----
else:
imap = IMAPSession(server, port, imapDebug, doExpunge)
+
+ # Load stats manager.
+ stats = Stats(options, message_db)
+
httpServer = UserInterfaceServer(options["html_ui", "port"])
httpServer.register(IMAPUserInterface(classifier, imap, pwd,
! IMAPSession, stats=stats))
launchBrowser=launchUI or options["html_ui", "launch_browser"]
if sleepTime:
Index: sb_pop3dnd.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_pop3dnd.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -C2 -d -r1.14 -r1.15
*** sb_pop3dnd.py 21 Dec 2004 23:21:22 -0000 1.14
--- sb_pop3dnd.py 22 Dec 2004 00:27:17 -0000 1.15
***************
*** 538,544 ****
class SpambayesInbox(SpambayesMailbox):
"""A special mailbox that holds status messages from SpamBayes."""
! def __init__(self, id, message_db):
IMAPMailbox.__init__(self, "INBOX", "spambayes", id)
! self.mdb = message_db
self.UID_validity = id
self.nextUID = 1
--- 538,544 ----
class SpambayesInbox(SpambayesMailbox):
"""A special mailbox that holds status messages from SpamBayes."""
! def __init__(self, id, state):
IMAPMailbox.__init__(self, "INBOX", "spambayes", id)
! self.mdb = state.mdb
self.UID_validity = id
self.nextUID = 1
***************
*** 547,560 ****
self.storage = {}
self.createMessages()
! s_thres = options["Categorization", "spam_cutoff"]
! u_thres = options["Categorization", "ham_cutoff"]
! fp_cost = options["TestDriver", "best_cutoff_fp_weight"]
! fn_cost = options["TestDriver", "best_cutoff_fn_weight"]
! unsure_cost = options["TestDriver", "best_cutoff_unsure_weight"]
! h_string = options["Headers", "header_ham_string"]
! s_string = options["Headers", "header_spam_string"]
! u_string = options["Headers", "header_unsure_string"]
! self.stats = Stats(s_thres, u_thres, message_db, h_string, u_string,
! s_string, fp_cost, fn_cost, unsure_cost)
def buildStatusMessage(self, body=False, headers=False):
--- 547,551 ----
self.storage = {}
self.createMessages()
! self.stats = state.stats
def buildStatusMessage(self, body=False, headers=False):
***************
*** 607,611 ****
msg.append('\r\n')
if body:
! msg.extend(s.GetStats(use_html=False))
return "\r\n".join(msg)
--- 598,602 ----
msg.append('\r\n')
if body:
! msg.extend(self.stats.GetStats(use_html=False))
return "\r\n".join(msg)
***************
*** 918,921 ****
--- 909,913 ----
work is done elsewhere. We do need to load the classifier,
though, and build the status strings."""
+ # Load token and message databases.
if not hasattr(self, "DBName"):
self.DBName, self.useDB = storage.database_type([])
***************
*** 924,927 ****
--- 916,924 ----
self.MDBName, self.useMDB = message.database_type()
self.mdb = message.open_storage(self.MDBName, self.useMDB)
+
+ # Load stats manager.
+ self.stats = Stats(options, self.mdb)
+
+ # Build status strings.
self.buildStatusStrings()
***************
*** 955,959 ****
"spam_to_train")
spam_train_box = SpambayesMailbox("TrainAsSpam", 3, spam_train_cache)
! inbox = SpambayesInbox(4, state.mdb)
spam_trainer = Trainer(spam_train_box, True)
--- 952,956 ----
"spam_to_train")
spam_train_box = SpambayesMailbox("TrainAsSpam", 3, spam_train_cache)
! inbox = SpambayesInbox(4, state)
spam_trainer = Trainer(spam_train_box, True)
Index: sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v
retrieving revision 1.34
retrieving revision 1.35
diff -C2 -d -r1.34 -r1.35
*** sb_server.py 21 Dec 2004 23:13:47 -0000 1.34
--- sb_server.py 22 Dec 2004 00:27:17 -0000 1.35
***************
*** 111,114 ****
--- 111,115 ----
import spambayes.message
from spambayes import i18n
+ from spambayes import Stats
from spambayes import Dibbler
from spambayes import storage
***************
*** 804,808 ****
self.DBName, self.useDB = storage.database_type([])
self.bayes = storage.open_storage(self.DBName, self.useDB)
!
self.buildStatusStrings()
--- 805,815 ----
self.DBName, self.useDB = storage.database_type([])
self.bayes = storage.open_storage(self.DBName, self.useDB)
! if not hasattr(self, "MBDName"):
! self.MDBName, self.useMDB = message.database_type()
! self.mdb = message.open_storage(self.MDBName, self.useMDB)
!
! # Load stats manager.
! self.stats = Stats(options, self.mdb)
!
self.buildStatusStrings()
From anadelonbrin at users.sourceforge.net Wed Dec 22 01:30:29 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 01:30:32 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 filter.py, 1.42,
1.43 manager.py, 1.103, 1.104
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1325/Outlook2000
Modified Files:
filter.py manager.py
Log Message:
Just pass the bayes_options object to the stats manager rather than lots of individual
options.
This has two consequences: we need to use the same values for the classification attribute
of messages (in filter.py) and we need to ensure that the bayes_options cutoff values
are always the same as the outlook config threshold values.
Index: filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
retrieving revision 1.42
retrieving revision 1.43
diff -C2 -d -r1.42 -r1.43
*** filter.py 21 Dec 2004 21:48:37 -0000 1.42
--- filter.py 22 Dec 2004 00:30:26 -0000 1.43
***************
*** 17,29 ****
disposition = "Yes"
attr_prefix = "spam"
! msg.c = "spam"
elif prob_perc >= config.unsure_threshold:
disposition = "Unsure"
attr_prefix = "unsure"
! msg.c = "unsure"
else:
disposition = "No"
attr_prefix = "ham"
! msg.c = "ham"
ms = mgr.message_store
--- 17,29 ----
disposition = "Yes"
attr_prefix = "spam"
! msg.c = mgr.bayes_options["Headers", "header_spam_string"]
elif prob_perc >= config.unsure_threshold:
disposition = "Unsure"
attr_prefix = "unsure"
! msg.c = mgr.bayes_options["Headers", "header_unsure_string"]
else:
disposition = "No"
attr_prefix = "ham"
! msg.c = mgr.bayes_options["Headers", "header_ham_string"]
ms = mgr.message_store
Index: manager.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
retrieving revision 1.103
retrieving revision 1.104
diff -C2 -d -r1.103 -r1.104
*** manager.py 21 Dec 2004 23:18:32 -0000 1.103
--- manager.py 22 Dec 2004 00:30:26 -0000 1.104
***************
*** 455,468 ****
self.ReportFatalStartupError("Failed to load bayes database")
self.classifier_data.InitNew()
! s_thres = self.config.filter.spam_threshold
! u_thres = self.config.filter.unsure_threshold
! fp_cost = bayes_options["TestDriver", "best_cutoff_fp_weight"]
! fn_cost = bayes_options["TestDriver", "best_cutoff_fn_weight"]
! unsure_cost = bayes_options["TestDriver",
! "best_cutoff_unsure_weight"]
! mdb = self.classifier_data.message_db
! self.stats = bayes_stats.Stats(s_thres, u_thres, mdb, "ham",
! "unsure", "spam", fp_cost, fn_cost,
! unsure_cost)
# Logging - this should be somewhere else.
--- 455,465 ----
self.ReportFatalStartupError("Failed to load bayes database")
self.classifier_data.InitNew()
! self.bayes_options = bayes_options
! bayes_options["Categorization", "spam_cutoff"] = \
! self.config.filter.spam_threshold
! bayes_options["Categorization", "ham_cutoff"] = \
! self.config.filter.unsure_threshold
! self.stats = bayes_stats.Stats(bayes_options,
! self.classifier_data.message_db)
# Logging - this should be somewhere else.
***************
*** 908,911 ****
--- 905,914 ----
# And re-save now, just incase Outlook dies on the way down.
self.SaveConfig()
+ # And update the cutoff values in bayes_options (which the
+ # stats use) to our thresholds.
+ bayes_options["Categorization", "spam_cutoff"] = \
+ self.config.filter.spam_threshold
+ bayes_options["Categorization", "ham_cutoff"] = \
+ self.config.filter.unsure_threshold
# And tell the addin that our filters may have changed.
if self.addin is not None:
From anadelonbrin at users.sourceforge.net Wed Dec 22 02:22:03 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 02:22:06 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 filter.py, 1.43,
1.44 manager.py, 1.104, 1.105 msgstore.py, 1.97, 1.98
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11996/Outlook2000
Modified Files:
filter.py manager.py msgstore.py
Log Message:
It makes life much simpler if the classification strings match the non-Outlook ones.
Thresholds are 0-100, cutoffs are 0.0-1.0 - need to convert between them, or everything
is spam!
Index: filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
retrieving revision 1.43
retrieving revision 1.44
diff -C2 -d -r1.43 -r1.44
*** filter.py 22 Dec 2004 00:30:26 -0000 1.43
--- filter.py 22 Dec 2004 01:22:00 -0000 1.44
***************
*** 17,29 ****
disposition = "Yes"
attr_prefix = "spam"
! msg.c = mgr.bayes_options["Headers", "header_spam_string"]
elif prob_perc >= config.unsure_threshold:
disposition = "Unsure"
attr_prefix = "unsure"
! msg.c = mgr.bayes_options["Headers", "header_unsure_string"]
else:
disposition = "No"
attr_prefix = "ham"
! msg.c = mgr.bayes_options["Headers", "header_ham_string"]
ms = mgr.message_store
--- 17,30 ----
disposition = "Yes"
attr_prefix = "spam"
! msg.c = mgr.bayes_options["Headers", "header_spam_string"][0]
elif prob_perc >= config.unsure_threshold:
disposition = "Unsure"
attr_prefix = "unsure"
! msg.c = mgr.bayes_options["Headers", "header_unsure_string"][0]
else:
disposition = "No"
attr_prefix = "ham"
! msg.c = mgr.bayes_options["Headers", "header_ham_string"][0]
! mgr.classifier_data.message_db.store_msg(msg)
ms = mgr.message_store
Index: manager.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
retrieving revision 1.104
retrieving revision 1.105
diff -C2 -d -r1.104 -r1.105
*** manager.py 22 Dec 2004 00:30:26 -0000 1.104
--- manager.py 22 Dec 2004 01:22:00 -0000 1.105
***************
*** 457,463 ****
self.bayes_options = bayes_options
bayes_options["Categorization", "spam_cutoff"] = \
! self.config.filter.spam_threshold
bayes_options["Categorization", "ham_cutoff"] = \
! self.config.filter.unsure_threshold
self.stats = bayes_stats.Stats(bayes_options,
self.classifier_data.message_db)
--- 457,465 ----
self.bayes_options = bayes_options
bayes_options["Categorization", "spam_cutoff"] = \
! self.config.filter.spam_threshold \
! / 100.0
bayes_options["Categorization", "ham_cutoff"] = \
! self.config.filter.unsure_threshold \
! / 100.0
self.stats = bayes_stats.Stats(bayes_options,
self.classifier_data.message_db)
***************
*** 908,914 ****
# stats use) to our thresholds.
bayes_options["Categorization", "spam_cutoff"] = \
! self.config.filter.spam_threshold
bayes_options["Categorization", "ham_cutoff"] = \
! self.config.filter.unsure_threshold
# And tell the addin that our filters may have changed.
if self.addin is not None:
--- 910,918 ----
# stats use) to our thresholds.
bayes_options["Categorization", "spam_cutoff"] = \
! self.config.filter.spam_threshold \
! / 100.0
bayes_options["Categorization", "ham_cutoff"] = \
! self.config.filter.unsure_threshold \
! / 100.0
# And tell the addin that our filters may have changed.
if self.addin is not None:
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.97
retrieving revision 1.98
diff -C2 -d -r1.97 -r1.98
*** msgstore.py 21 Dec 2004 21:48:37 -0000 1.97
--- msgstore.py 22 Dec 2004 01:22:00 -0000 1.98
***************
*** 812,815 ****
--- 812,816 ----
self.t = None
self.c = None
+ self.date_modified = None
self.original_folder = None
From anadelonbrin at users.sourceforge.net Wed Dec 22 02:29:16 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 02:29:19 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_server.py,1.35,1.36
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13185/scripts
Modified Files:
sb_server.py
Log Message:
Fix bad import found by Sean Darcy.
Index: sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -C2 -d -r1.35 -r1.36
*** sb_server.py 22 Dec 2004 00:27:17 -0000 1.35
--- sb_server.py 22 Dec 2004 01:29:14 -0000 1.36
***************
*** 109,117 ****
from email.Header import Header
- import spambayes.message
from spambayes import i18n
from spambayes import Stats
from spambayes import Dibbler
from spambayes import storage
from spambayes.FileCorpus import FileCorpus, ExpiryFileCorpus
from spambayes.FileCorpus import FileMessageFactory, GzipFileMessageFactory
--- 109,117 ----
from email.Header import Header
from spambayes import i18n
from spambayes import Stats
from spambayes import Dibbler
from spambayes import storage
+ from spambayes import message
from spambayes.FileCorpus import FileCorpus, ExpiryFileCorpus
from spambayes.FileCorpus import FileMessageFactory, GzipFileMessageFactory
***************
*** 494,498 ****
try:
msg = email.message_from_string(messageText,
! _class=spambayes.message.SBHeaderMessage)
msg.setId(state.getNewMessageName())
# Now find the spam disposition and add the header.
--- 494,498 ----
try:
msg = email.message_from_string(messageText,
! _class=message.SBHeaderMessage)
msg.setId(state.getNewMessageName())
# Now find the spam disposition and add the header.
***************
*** 562,566 ****
# This is one case where an unqualified 'except' is OK, 'cos
# anything's better than destroying people's email...
! messageText, details = spambayes.message.\
insert_exception_header(messageText)
--- 562,566 ----
# This is one case where an unqualified 'except' is OK, 'cos
# anything's better than destroying people's email...
! messageText, details = message.\
insert_exception_header(messageText)
From kennypitt at hotmail.com Tue Dec 21 16:05:33 2004
From: kennypitt at hotmail.com (Kenny Pitt)
Date: Wed Dec 22 02:32:05 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_server.py,1.32,1.33
In-Reply-To:
Message-ID:
Tony Meyer wrote:
> Kenny's fix worked if there was more than +OK, but not if there was
> just +OK. Fix the fix so that both cases work.
Oops, sorry, that was my limited Python experience showing through. I
assumed that split() would just return None for the second value if there
was no separator. Thanks for covering me!
BTW, any idea what might have happened to the stats page in sb_server? I'm
looking for the problem right now, but every time I try to view stats I get
a "500 Server error" with the following traceback:
"""
Traceback (most recent call last):
File "C:\src\python\spambayes\spambayes\Dibbler.py", line 461, in
found_terminator
getattr(plugin, name)(**params)
File "C:\src\python\spambayes\spambayes\UserInterface.py", line 915, in
onStats
s = Stats.Stats()
File "C:\src\python\spambayes\spambayes\Stats.py", line 50, in __init__
self.CalculateStats()
File "C:\src\python\spambayes\spambayes\Stats.py", line 72, in
CalculateStats
msginfoDB._getState(m)
AttributeError: 'MessageInfoDB' object has no attribute '_getState'
"""
I just upgraded to Python 2.4 Final yesterday, so don't know if that might
have something to do with it.
--
Kenny Pitt
From anadelonbrin at users.sourceforge.net Wed Dec 22 02:50:15 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 02:50:17 2004
Subject: [Spambayes-checkins] spambayes/spambayes/test test_message.py, 1.3,
1.4
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes/test
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16968/spambayes/test
Modified Files:
test_message.py
Log Message:
Update to reflect modified message info db.
Index: test_message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_message.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** test_message.py 17 Dec 2004 01:22:28 -0000 1.3
--- test_message.py 22 Dec 2004 01:50:12 -0000 1.4
***************
*** 4,7 ****
--- 4,8 ----
import sys
import math
+ import time
import email
import unittest
***************
*** 566,570 ****
correct = [(att, getattr(msg, att)) \
for att in msg.stored_attributes]
! self.assertEqual(self.db.db[msg.id], correct)
def _fake_store(self):
--- 567,575 ----
correct = [(att, getattr(msg, att)) \
for att in msg.stored_attributes]
! db_version = dict(self.db.db[msg.id])
! correct_version = dict(correct)
! self.assertEqual(db_version["date_modified"], time.time())
! del db_version["date_modified"]
! self.assertEqual(db_version, correct_version)
def _fake_store(self):
From anadelonbrin at users.sourceforge.net Wed Dec 22 02:52:05 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 02:52:08 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_pop3dnd.py, 1.15,
1.16 sb_server.py, 1.36, 1.37
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17316/scripts
Modified Files:
sb_pop3dnd.py sb_server.py
Log Message:
Sorry - I forgot to run the tests before the last checkins, so there are obvious failures.
Fix a random dot in sb_pop3nd.py and correct the importing in sb_server.
Index: sb_pop3dnd.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_pop3dnd.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** sb_pop3dnd.py 22 Dec 2004 00:27:17 -0000 1.15
--- sb_pop3dnd.py 22 Dec 2004 01:51:49 -0000 1.16
***************
*** 303,307 ****
! class IM.APFileMessage(IMAPMessage, FileCorpus.FileMessage):
'''IMAP Message that persists as a file system artifact.'''
--- 303,307 ----
! class IMAPFileMessage(IMAPMessage, FileCorpus.FileMessage):
'''IMAP Message that persists as a file system artifact.'''
Index: sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v
retrieving revision 1.36
retrieving revision 1.37
diff -C2 -d -r1.36 -r1.37
*** sb_server.py 22 Dec 2004 01:29:14 -0000 1.36
--- sb_server.py 22 Dec 2004 01:51:49 -0000 1.37
***************
*** 109,117 ****
from email.Header import Header
from spambayes import i18n
from spambayes import Stats
from spambayes import Dibbler
from spambayes import storage
- from spambayes import message
from spambayes.FileCorpus import FileCorpus, ExpiryFileCorpus
from spambayes.FileCorpus import FileMessageFactory, GzipFileMessageFactory
--- 109,117 ----
from email.Header import Header
+ import spambayes.message
from spambayes import i18n
from spambayes import Stats
from spambayes import Dibbler
from spambayes import storage
from spambayes.FileCorpus import FileCorpus, ExpiryFileCorpus
from spambayes.FileCorpus import FileMessageFactory, GzipFileMessageFactory
***************
*** 494,498 ****
try:
msg = email.message_from_string(messageText,
! _class=message.SBHeaderMessage)
msg.setId(state.getNewMessageName())
# Now find the spam disposition and add the header.
--- 494,498 ----
try:
msg = email.message_from_string(messageText,
! _class=spambayes.message.SBHeaderMessage)
msg.setId(state.getNewMessageName())
# Now find the spam disposition and add the header.
***************
*** 562,566 ****
# This is one case where an unqualified 'except' is OK, 'cos
# anything's better than destroying people's email...
! messageText, details = message.\
insert_exception_header(messageText)
--- 562,566 ----
# This is one case where an unqualified 'except' is OK, 'cos
# anything's better than destroying people's email...
! messageText, details = spambayes.message.\
insert_exception_header(messageText)
***************
*** 806,814 ****
self.bayes = storage.open_storage(self.DBName, self.useDB)
if not hasattr(self, "MBDName"):
! self.MDBName, self.useMDB = message.database_type()
! self.mdb = message.open_storage(self.MDBName, self.useMDB)
# Load stats manager.
! self.stats = Stats(options, self.mdb)
self.buildStatusStrings()
--- 806,814 ----
self.bayes = storage.open_storage(self.DBName, self.useDB)
if not hasattr(self, "MBDName"):
! self.MDBName, self.useMDB = spambayes.message.database_type()
! self.mdb = spambayes.message.open_storage(self.MDBName, self.useMDB)
# Load stats manager.
! self.stats = Stats.Stats(options, self.mdb)
self.buildStatusStrings()
From anadelonbrin at users.sourceforge.net Wed Dec 22 04:32:22 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 04:32:27 2004
Subject: [Spambayes-checkins] spambayes/spambayes Dibbler.py,1.15,1.16
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv644/spambayes
Modified Files:
Dibbler.py
Log Message:
rstrip(chars) isn't available pre 2.2.2, and we say we support 2.2, so add a function to
do the work if rstrip(chars) isn't available.
This addresses the concern at http://entrian.com/sbwiki/Python22Compatibility
Index: Dibbler.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Dibbler.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** Dibbler.py 6 Dec 2004 01:37:43 -0000 1.15
--- Dibbler.py 22 Dec 2004 03:32:19 -0000 1.16
***************
*** 179,182 ****
--- 179,191 ----
True, False = 1, 0
+ try:
+ "".rstrip("abc")
+ except TypeError:
+ # rstrip(chars) requires Python 2.2.2 or higher. Apart from that
+ # we probably work with Python 2.2 (and say we do), so provide the
+ # ability to do this for that case.
+ RSTRIP_CHARS_AVAILABLE = False
+ else:
+ RSTRIP_CHARS_AVAILABLE = True
class BrighterAsyncChat(asynchat.async_chat):
***************
*** 594,598 ****
minutes from now."""
timeString = time.asctime(time.localtime(time.time() + 20*60))
! return base64.encodestring(timeString).rstrip('\n=')
def _isValidNonce(self, nonce):
--- 603,618 ----
minutes from now."""
timeString = time.asctime(time.localtime(time.time() + 20*60))
! if RSTRIP_CHARS_AVAILABLE:
! return base64.encodestring(timeString).rstrip('\n=')
! else:
! # Python pre 2.2.2, so can't do a rstrip(chars). Do it
! # manually instead.
! def rstrip(s, chars):
! if not s:
! return s
! if s[-1] in chars:
! return rstrip(s[:-1])
! return s
! return rstrip(base64.encodestring(timeString), '\n=')
def _isValidNonce(self, nonce):
From anadelonbrin at users.sourceforge.net Wed Dec 22 04:34:58 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 22 04:35:00 2004
Subject: [Spambayes-checkins] spambayes/spambayes Dibbler.py, 1.13.4.2,
1.13.4.3
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv987/spambayes
Modified Files:
Tag: release_1_0-branch
Dibbler.py
Log Message:
Backport 2.2.1 compatibility.
Index: Dibbler.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Dibbler.py,v
retrieving revision 1.13.4.2
retrieving revision 1.13.4.3
diff -C2 -d -r1.13.4.2 -r1.13.4.3
*** Dibbler.py 20 Dec 2004 03:53:55 -0000 1.13.4.2
--- Dibbler.py 22 Dec 2004 03:34:55 -0000 1.13.4.3
***************
*** 179,182 ****
--- 179,191 ----
True, False = 1, 0
+ try:
+ "".rstrip("abc")
+ except TypeError:
+ # rstrip(chars) requires Python 2.2.2 or higher. Apart from that
+ # we probably work with Python 2.2 (and say we do), so provide the
+ # ability to do this for that case.
+ RSTRIP_CHARS_AVAILABLE = False
+ else:
+ RSTRIP_CHARS_AVAILABLE = True
class BrighterAsyncChat(asynchat.async_chat):
***************
*** 594,598 ****
minutes from now."""
timeString = time.asctime(time.localtime(time.time() + 20*60))
! return base64.encodestring(timeString).rstrip('\n=')
def _isValidNonce(self, nonce):
--- 603,618 ----
minutes from now."""
timeString = time.asctime(time.localtime(time.time() + 20*60))
! if RSTRIP_CHARS_AVAILABLE:
! return base64.encodestring(timeString).rstrip('\n=')
! else:
! # Python pre 2.2.2, so can't do a rstrip(chars). Do it
! # manually instead.
! def rstrip(s, chars):
! if not s:
! return s
! if s[-1] in chars:
! return rstrip(s[:-1])
! return s
! return rstrip(base64.encodestring(timeString), '\n=')
def _isValidNonce(self, nonce):
From kpitt at users.sourceforge.net Wed Dec 22 19:37:39 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Wed Dec 22 19:37:42 2004
Subject: [Spambayes-checkins] spambayes/spambayes message.py,1.64,1.65
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31065
Modified Files:
message.py
Log Message:
Stats.CalculatePersistentStats tests for old-format messages in the message
info database by checking if the date_modified attribute of the message is
None. Unfortunately, old-format messages didn't even have a date_modified
attribute, which caused an error as soon as the from_date of the Stats
object was set to a non-None value. Correct this by always initializing
date_modified to None in the Message.__init__ function.
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.64
retrieving revision 1.65
diff -C2 -d -r1.64 -r1.65
*** message.py 21 Dec 2004 23:12:11 -0000 1.64
--- message.py 22 Dec 2004 18:37:36 -0000 1.65
***************
*** 171,175 ****
def store_msg(self, msg):
if self.db is not None:
! attributes = [("date_modified", time.time())]
for att in msg.stored_attributes:
attributes.append((att, getattr(msg, att)))
--- 171,176 ----
def store_msg(self, msg):
if self.db is not None:
! msg.date_modified = time.time()
! attributes = []
for att in msg.stored_attributes:
attributes.append((att, getattr(msg, att)))
***************
*** 288,296 ****
nm, typ = database_type()
self.message_info_db = open_storage(nm, typ)
! self.stored_attributes = ['c', 't',]
self.getDBKey = self.getId
self.id = None
self.c = None
self.t = None
if id is not None:
--- 289,298 ----
nm, typ = database_type()
self.message_info_db = open_storage(nm, typ)
! self.stored_attributes = ['c', 't', 'date_modified', ]
self.getDBKey = self.getId
self.id = None
self.c = None
self.t = None
+ self.date_modified = None
if id is not None:
From kpitt at users.sourceforge.net Wed Dec 22 19:55:57 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Wed Dec 22 19:56:11 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs dialog_map.py,
1.42, 1.43
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4639/Outlook2000/dialogs
Modified Files:
dialog_map.py
Log Message:
Add a button on the Statistics tab to reset the Outlook statistics. Also
display the date when the statistics were last reset.
Index: dialog_map.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dialog_map.py,v
retrieving revision 1.42
retrieving revision 1.43
diff -C2 -d -r1.42 -r1.43
*** dialog_map.py 5 Dec 2004 23:28:50 -0000 1.42
--- dialog_map.py 22 Dec 2004 18:55:54 -0000 1.43
***************
*** 17,26 ****
# "dialog specific" processors:
class StatsProcessor(ControlProcessor):
def Init(self):
! text = "\n".join(self.window.manager.stats.GetStats())
win32gui.SendMessage(self.GetControl(), win32con.WM_SETTEXT, 0, text)
! def GetPopupHelpText(self, cid):
! return "Displays statistics on mail processed by SpamBayes"
class VersionStringProcessor(ControlProcessor):
--- 17,61 ----
# "dialog specific" processors:
class StatsProcessor(ControlProcessor):
+ def __init__(self, window, control_ids):
+ self.button_id = control_ids[1]
+ self.reset_date_id = control_ids[2]
+ ControlProcessor.__init__(self, window, control_ids)
+ self.stats = self.window.manager.stats
+
def Init(self):
! text = "\n".join(self.stats.GetStats())
win32gui.SendMessage(self.GetControl(), win32con.WM_SETTEXT, 0, text)
! date_label = self.GetControl(self.reset_date_id)
! if self.stats.from_date:
! from time import localtime, strftime
! reset_date = localtime(self.stats.from_date)
! date_string = strftime("%a, %d %b %Y %I:%M:%S %p", reset_date)
! else:
! date_string = "None"
! win32gui.SendMessage(date_label, win32con.WM_SETTEXT, 0, date_string)
!
! def OnCommand(self, wparam, lparam):
! id = win32api.LOWORD(wparam)
! if id == self.button_id:
! self.ResetStatistics()
!
! def GetPopupHelpText(self, idFrom):
! if idFrom == self.control_id:
! return "Displays statistics on mail processed by SpamBayes"
! elif idFrom == self.button_id:
! return "Resets all SpamBayes statistics to zero"
! elif idFrom == self.reset_date_id:
! return "The date and time when the SpamBayes statistics were last reset"
!
! def ResetStatistics(self):
! question = "This will reset all your saved statistics to zero.\r\n\r\n" \
! "Are you sure you wish to reset the statistics?"
! flags = win32con.MB_ICONQUESTION | win32con.MB_YESNO | win32con.MB_DEFBUTTON2
! if win32gui.MessageBox(self.window.hwnd,
! question, "SpamBayes", flags) == win32con.IDYES:
! self.stats.Reset()
! self.stats.ResetTotal(True)
! self.Init() # update the statistics display
class VersionStringProcessor(ControlProcessor):
***************
*** 476,480 ****
),
"IDD_STATISTICS" : (
! (StatsProcessor, "IDC_STATISTICS"),
),
"IDD_ADVANCED" : (
--- 511,516 ----
),
"IDD_STATISTICS" : (
! (StatsProcessor, "IDC_STATISTICS IDC_BUT_RESET_STATS " \
! "IDC_LAST_RESET_DATE"),
),
"IDD_ADVANCED" : (
From kpitt at users.sourceforge.net Wed Dec 22 19:55:57 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Wed Dec 22 19:56:11 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs/resources
dialogs.h, 1.22, 1.23 dialogs.rc, 1.48, 1.49
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4639/Outlook2000/dialogs/resources
Modified Files:
dialogs.h dialogs.rc
Log Message:
Add a button on the Statistics tab to reset the Outlook statistics. Also
display the date when the statistics were last reset.
Index: dialogs.h
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/dialogs.h,v
retrieving revision 1.22
retrieving revision 1.23
diff -C2 -d -r1.22 -r1.23
*** dialogs.h 11 Nov 2004 21:55:46 -0000 1.22
--- dialogs.h 22 Dec 2004 18:55:54 -0000 1.23
***************
*** 1,4 ****
//{{NO_DEPENDENCIES}}
! // Microsoft Visual C++ generated include file.
// Used by dialogs.rc
//
--- 1,4 ----
//{{NO_DEPENDENCIES}}
! // Microsoft Developer Studio generated include file.
// Used by dialogs.rc
//
***************
*** 100,103 ****
--- 100,105 ----
#define IDC_EDIT1 1094
#define IDC_STATISTICS 1095
+ #define IDC_BUT_RESET_STATS 1096
+ #define IDC_LAST_RESET_DATE 1097
// Next default values for new objects
***************
*** 107,111 ****
#define _APS_NEXT_RESOURCE_VALUE 128
#define _APS_NEXT_COMMAND_VALUE 40001
! #define _APS_NEXT_CONTROL_VALUE 1096
#define _APS_NEXT_SYMED_VALUE 101
#endif
--- 109,113 ----
#define _APS_NEXT_RESOURCE_VALUE 128
#define _APS_NEXT_COMMAND_VALUE 40001
! #define _APS_NEXT_CONTROL_VALUE 1098
#define _APS_NEXT_SYMED_VALUE 101
#endif
Index: dialogs.rc
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/dialogs.rc,v
retrieving revision 1.48
retrieving revision 1.49
diff -C2 -d -r1.48 -r1.49
*** dialogs.rc 11 Nov 2004 21:55:46 -0000 1.48
--- dialogs.rc 22 Dec 2004 18:55:55 -0000 1.49
***************
*** 1,3 ****
! // Microsoft Visual C++ generated resource script.
//
#include "dialogs.h"
--- 1,3 ----
! //Microsoft Developer Studio generated resource script.
//
#include "dialogs.h"
***************
*** 29,36 ****
IDD_ADVANCED DIALOGEX 0, 0, 248, 209
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Advanced"
! FONT 8, "Tahoma", 400, 0, 0x0
BEGIN
GROUPBOX "Filter timer",IDC_STATIC,7,3,234,117
--- 29,36 ----
IDD_ADVANCED DIALOGEX 0, 0, 248, 209
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Advanced"
! FONT 8, "Tahoma", 400
BEGIN
GROUPBOX "Filter timer",IDC_STATIC,7,3,234,117
***************
*** 54,83 ****
END
! IDD_STATISTICS DIALOGEX 0, 0, 248, 209
! STYLE DS_SETFONT | WS_CHILD
CAPTION "Statistics"
! FONT 8, "Tahoma", 400, 0, 0x0
BEGIN
! GROUPBOX "Statistics",IDC_STATIC,7,3,234,201
LTEXT "some stats\nand some more\nline 3\nline 4\nline 5",
! IDC_STATISTICS,12,12,223,186
END
IDD_MANAGER DIALOGEX 0, 0, 275, 260
! STYLE DS_SETFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
! CAPTION "SpamBayes"
! FONT 8, "Tahoma", 400, 0, 0x0
BEGIN
DEFPUSHBUTTON "Close",IDOK,216,239,50,14
PUSHBUTTON "Cancel",IDCANCEL,155,239,50,14,NOT WS_VISIBLE
CONTROL "",IDC_TAB,"SysTabControl32",0x0,8,7,258,228
! PUSHBUTTON "About",IDC_ABOUT_BTN,9,239,50,14
END
IDD_FILTER_SPAM DIALOGEX 0, 0, 251, 147
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Spam"
! FONT 8, "Tahoma", 400, 0, 0x0
BEGIN
LTEXT "Filter the following folders as messages arrive",
--- 54,86 ----
END
! IDD_STATISTICS DIALOG DISCARDABLE 0, 0, 248, 209
! STYLE WS_CHILD | WS_CAPTION
CAPTION "Statistics"
! FONT 8, "Tahoma"
BEGIN
! GROUPBOX "Statistics",IDC_STATIC,7,3,241,181
LTEXT "some stats\nand some more\nline 3\nline 4\nline 5",
! IDC_STATISTICS,12,12,230,166
! PUSHBUTTON "Reset Statistics",IDC_BUT_RESET_STATS,178,190,70,14
! LTEXT "Last reset:",IDC_STATIC,7,193,36,8
! LTEXT "<<>>",IDC_LAST_RESET_DATE,47,193,107,8
END
IDD_MANAGER DIALOGEX 0, 0, 275, 260
! STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
! CAPTION "SpamBayes Manager"
! FONT 8, "Tahoma", 400
BEGIN
DEFPUSHBUTTON "Close",IDOK,216,239,50,14
PUSHBUTTON "Cancel",IDCANCEL,155,239,50,14,NOT WS_VISIBLE
CONTROL "",IDC_TAB,"SysTabControl32",0x0,8,7,258,228
! PUSHBUTTON "About",IDC_ABOUT_BTN,8,239,50,14
END
IDD_FILTER_SPAM DIALOGEX 0, 0, 251, 147
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Spam"
! FONT 8, "Tahoma", 400
BEGIN
LTEXT "Filter the following folders as messages arrive",
***************
*** 106,112 ****
IDD_FILTER_UNSURE DIALOGEX 0, 0, 249, 124
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Possible Spam"
! FONT 8, "Tahoma", 400, 0, 0x0
BEGIN
LTEXT "To be considered uncertain, a message must score at least",
--- 109,115 ----
IDD_FILTER_UNSURE DIALOGEX 0, 0, 249, 124
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Possible Spam"
! FONT 8, "Tahoma", 400
BEGIN
LTEXT "To be considered uncertain, a message must score at least",
***************
*** 128,135 ****
IDD_DIAGNOSTIC DIALOGEX 0, 0, 183, 98
! STYLE DS_SETFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Diagnostics"
! FONT 8, "Tahoma", 400, 0, 0x0
BEGIN
LTEXT "These advanced options are for diagnostic or debugging purposes only. You should only change these options if specifically asked to, or you know exactly what they mean.",
--- 131,138 ----
IDD_DIAGNOSTIC DIALOGEX 0, 0, 183, 98
! STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Diagnostics"
! FONT 8, "Tahoma", 400
BEGIN
LTEXT "These advanced options are for diagnostic or debugging purposes only. You should only change these options if specifically asked to, or you know exactly what they mean.",
***************
*** 145,152 ****
IDD_WIZARD DIALOGEX 0, 0, 384, 190
! STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "SpamBayes Configuration Wizard"
! FONT 8, "Tahoma", 400, 0, 0x0
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,328,173,50,14
--- 148,155 ----
IDD_WIZARD DIALOGEX 0, 0, 384, 190
! STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "SpamBayes Configuration Wizard"
! FONT 8, "Tahoma", 400
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,328,173,50,14
***************
*** 159,165 ****
IDD_WIZARD_WELCOME DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "Welcome to the SpamBayes configuration wizard",
--- 162,168 ----
IDD_WIZARD_WELCOME DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Welcome to the SpamBayes configuration wizard",
***************
*** 184,190 ****
IDD_WIZARD_FINISHED_UNTRAINED DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
--- 187,193 ----
IDD_WIZARD_FINISHED_UNTRAINED DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
***************
*** 202,208 ****
IDD_WIZARD_FOLDERS_REST DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_SPAM,208,85,60,15
--- 205,211 ----
IDD_WIZARD_FOLDERS_REST DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_SPAM,208,85,60,15
***************
*** 222,228 ****
IDD_WIZARD_FOLDERS_WATCH DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_WATCH,225,134,50,14
--- 225,231 ----
IDD_WIZARD_FOLDERS_WATCH DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_WATCH,225,134,50,14
***************
*** 240,246 ****
IDD_WIZARD_FINISHED_UNCONFIGURED DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "Configuration cancelled",IDC_STATIC,20,4,247,14
--- 243,249 ----
IDD_WIZARD_FINISHED_UNCONFIGURED DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Configuration cancelled",IDC_STATIC,20,4,247,14
***************
*** 252,258 ****
IDD_WIZARD_FOLDERS_TRAIN DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_HAM,208,49,60,15
--- 255,261 ----
IDD_WIZARD_FOLDERS_TRAIN DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_HAM,208,49,60,15
***************
*** 276,282 ****
IDD_WIZARD_TRAIN DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "Training",-1,20,4,247,14
--- 279,285 ----
IDD_WIZARD_TRAIN DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Training",-1,20,4,247,14
***************
*** 289,295 ****
IDD_WIZARD_FINISHED_TRAINED DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
--- 292,298 ----
IDD_WIZARD_FINISHED_TRAINED DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
***************
*** 303,309 ****
IDD_WIZARD_TRAINING_IS_IMPORTANT DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "SpamBayes will not be effective until it is trained.",
--- 306,312 ----
IDD_WIZARD_TRAINING_IS_IMPORTANT DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "SpamBayes will not be effective until it is trained.",
***************
*** 326,332 ****
IDD_WIZARD_FINISHED_TRAIN_LATER DIALOGEX 0, 0, 284, 162
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "Configuration suspended",IDC_STATIC,20,4,247,14
--- 329,335 ----
IDD_WIZARD_FINISHED_TRAIN_LATER DIALOGEX 0, 0, 284, 162
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Configuration suspended",IDC_STATIC,20,4,247,14
***************
*** 352,356 ****
#ifdef APSTUDIO_INVOKED
! GUIDELINES DESIGNINFO
BEGIN
IDD_ADVANCED, DIALOG
--- 355,359 ----
#ifdef APSTUDIO_INVOKED
! GUIDELINES DESIGNINFO MOVEABLE PURE
BEGIN
IDD_ADVANCED, DIALOG
***************
*** 472,477 ****
//
! IDB_SBLOGO BITMAP "sblogo.bmp"
! IDB_SBWIZLOGO BITMAP "sbwizlogo.bmp"
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
--- 475,480 ----
//
! IDB_SBLOGO BITMAP MOVEABLE PURE "sblogo.bmp"
! IDB_SBWIZLOGO BITMAP MOVEABLE PURE "sbwizlogo.bmp"
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
***************
*** 493,498 ****
IDD_GENERAL DIALOGEX 0, 0, 253, 210
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_VISIBLE | WS_CAPTION |
! WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "General"
--- 496,500 ----
IDD_GENERAL DIALOGEX 0, 0, 253, 210
! STYLE DS_MODALFRAME | WS_CHILD | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "General"
***************
*** 517,521 ****
IDD_TRAINING DIALOGEX 0, 0, 252, 210
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Training"
--- 519,523 ----
IDD_TRAINING DIALOGEX 0, 0, 252, 210
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Training"
***************
*** 553,558 ****
IDC_BUT_TRAIN_TO_SPAM_FOLDER,"Button",BS_AUTOCHECKBOX |
BS_MULTILINE | WS_TABSTOP,11,163,204,16
! LTEXT "Clicking 'Spam' button should",IDC_STATIC,10,183,104,
! 10
COMBOBOX IDC_DEL_SPAM_RS,127,180,114,54,CBS_DROPDOWNLIST |
WS_VSCROLL | WS_TABSTOP
--- 555,559 ----
IDC_BUT_TRAIN_TO_SPAM_FOLDER,"Button",BS_AUTOCHECKBOX |
BS_MULTILINE | WS_TABSTOP,11,163,204,16
! LTEXT "Clicking 'Spam' button should",IDC_STATIC,10,183,104,10
COMBOBOX IDC_DEL_SPAM_RS,127,180,114,54,CBS_DROPDOWNLIST |
WS_VSCROLL | WS_TABSTOP
***************
*** 560,564 ****
IDD_FILTER_NOW DIALOGEX 0, 0, 244, 185
! STYLE DS_SETFONT | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Filter Now"
--- 561,565 ----
IDD_FILTER_NOW DIALOGEX 0, 0, 244, 185
! STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Filter Now"
***************
*** 589,593 ****
IDD_FILTER DIALOGEX 0, 0, 249, 209
! STYLE DS_SETFONT | DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Filtering"
--- 590,594 ----
IDD_FILTER DIALOGEX 0, 0, 249, 209
! STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Filtering"
***************
*** 635,641 ****
IDD_FOLDER_SELECTOR DIALOGEX 0, 0, 247, 215
! STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
! FONT 8, "Tahoma", 0, 0, 0x0
BEGIN
LTEXT "&Folders:",IDC_STATIC,7,7,47,9
--- 636,642 ----
IDD_FOLDER_SELECTOR DIALOGEX 0, 0, 247, 215
! STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
! FONT 8, "Tahoma"
BEGIN
LTEXT "&Folders:",IDC_STATIC,7,7,47,9
***************
*** 661,665 ****
#ifdef APSTUDIO_INVOKED
! GUIDELINES DESIGNINFO
BEGIN
IDD_GENERAL, DIALOG
--- 662,666 ----
#ifdef APSTUDIO_INVOKED
! GUIDELINES DESIGNINFO MOVEABLE PURE
BEGIN
IDD_GENERAL, DIALOG
***************
*** 700,704 ****
//
! IDB_FOLDERS BITMAP "folders.bmp"
#ifdef APSTUDIO_INVOKED
--- 701,705 ----
//
! IDB_FOLDERS BITMAP MOVEABLE PURE "folders.bmp"
#ifdef APSTUDIO_INVOKED
***************
*** 708,717 ****
//
! 1 TEXTINCLUDE
BEGIN
"dialogs.h\0"
END
! 2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
--- 709,718 ----
//
! 1 TEXTINCLUDE MOVEABLE PURE
BEGIN
"dialogs.h\0"
END
! 2 TEXTINCLUDE MOVEABLE PURE
BEGIN
"#include ""winres.h""\r\n"
***************
*** 720,724 ****
END
! 3 TEXTINCLUDE
BEGIN
"\r\n"
--- 721,725 ----
END
! 3 TEXTINCLUDE MOVEABLE PURE
BEGIN
"\r\n"
From anadelonbrin at users.sourceforge.net Thu Dec 23 03:05:18 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 03:05:22 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 filter.py, 1.45,
1.46 manager.py, 1.105, 1.106
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15578/Outlook2000
Modified Files:
filter.py manager.py
Log Message:
Only store the classification of the message in the messageinfo db if 'all_actions'
is true. This does not include using the spam/not spam buttons, which is necessary
because otherwise our statistics will always reflect corrected results, not the initial
ones.
Use constants for the classification strings to avoid problems with people changing
option values when they shouldn't.
Index: filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
retrieving revision 1.45
retrieving revision 1.46
diff -C2 -d -r1.45 -r1.46
*** filter.py 23 Dec 2004 01:50:47 -0000 1.45
--- filter.py 23 Dec 2004 02:05:14 -0000 1.46
***************
*** 17,31 ****
disposition = "Yes"
attr_prefix = "spam"
! msg.c = mgr.bayes_options["Headers", "header_spam_string"][0]
elif prob_perc >= config.unsure_threshold:
disposition = "Unsure"
attr_prefix = "unsure"
! msg.c = mgr.bayes_options["Headers", "header_unsure_string"][0]
else:
disposition = "No"
attr_prefix = "ham"
! msg.c = mgr.bayes_options["Headers", "header_ham_string"][0]
! mgr.classifier_data.message_db.store_msg(msg)
! mgr.classifier_data.dirty = True
ms = mgr.message_store
--- 17,32 ----
disposition = "Yes"
attr_prefix = "spam"
! if all_actions:
! msg.c = mgr.bayes_message.PERSISTENT_SPAM_STRING
elif prob_perc >= config.unsure_threshold:
disposition = "Unsure"
attr_prefix = "unsure"
! if all_actions:
! msg.c = mgr.bayes_message.PERSISTENT_UNSURE_STRING
else:
disposition = "No"
attr_prefix = "ham"
! if all_actions:
! msg.c = mgr.bayes_message.PERSISTENT_HAM_STRING
ms = mgr.message_store
***************
*** 49,53 ****
if all_actions:
msg.RememberMessageCurrentFolder()
- mgr.classifier_data.message_db.store_msg(msg)
msg.Save()
break
--- 50,53 ----
***************
*** 111,114 ****
--- 111,116 ----
if all_actions:
mgr.stats.RecordClassification(prob)
+ mgr.classifier_data.message_db.store_msg(msg)
+ mgr.classifier_data.dirty = True
return disposition
except:
Index: manager.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
retrieving revision 1.105
retrieving revision 1.106
diff -C2 -d -r1.105 -r1.106
*** manager.py 22 Dec 2004 01:22:00 -0000 1.105
--- manager.py 23 Dec 2004 02:05:14 -0000 1.106
***************
*** 456,459 ****
--- 456,460 ----
self.classifier_data.InitNew()
self.bayes_options = bayes_options
+ self.bayes_message = bayes_message
bayes_options["Categorization", "spam_cutoff"] = \
self.config.filter.spam_threshold \
From anadelonbrin at users.sourceforge.net Thu Dec 23 02:50:50 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 03:05:24 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 filter.py,1.44,1.45
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv12322/Outlook2000
Modified Files:
filter.py
Log Message:
Note that the messageinfo db needs saving after making changes.
Index: filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -C2 -d -r1.44 -r1.45
*** filter.py 22 Dec 2004 01:22:00 -0000 1.44
--- filter.py 23 Dec 2004 01:50:47 -0000 1.45
***************
*** 27,30 ****
--- 27,31 ----
msg.c = mgr.bayes_options["Headers", "header_ham_string"][0]
mgr.classifier_data.message_db.store_msg(msg)
+ mgr.classifier_data.dirty = True
ms = mgr.message_store
From anadelonbrin at users.sourceforge.net Thu Dec 23 02:48:29 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 03:05:25 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.145,1.146
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11779/Outlook2000
Modified Files:
addin.py
Log Message:
Need to store training status when using the spam/not spam buttons.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.145
retrieving revision 1.146
diff -C2 -d -r1.145 -r1.146
*** addin.py 21 Dec 2004 21:48:30 -0000 1.145
--- addin.py 23 Dec 2004 01:48:26 -0000 1.146
***************
*** 697,700 ****
--- 697,703 ----
self.manager.stats.RecordTraining(False,
self.manager.score(msgstore_message))
+ msgstore_message.t = True
+ self.manager.classifier_data.message_db.store_msg(msg)
+ self.manager.classifier_data.dirty = True
# Record the original folder, in case this message is not where
# it was after filtering, or has never been filtered.
***************
*** 765,768 ****
--- 768,774 ----
self.manager.stats.RecordTraining(True,
self.manager.score(msgstore_message))
+ msgstore_message.t = False
+ self.manager.classifier_data.message_db.store_msg(msg)
+ self.manager.classifier_data.dirty = True
# Must train before moving, else we lose the message!
print "Recovering to folder '%s' and ham training message '%s' - " % (restore_folder.name, subject),
From anadelonbrin at users.sourceforge.net Thu Dec 23 02:42:43 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 03:05:25 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs dialog_map.py,
1.43, 1.44
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10561/Outlook2000/dialogs
Modified Files:
dialog_map.py
Log Message:
I like "last reset: never" more than "last reset: none".
Index: dialog_map.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dialog_map.py,v
retrieving revision 1.43
retrieving revision 1.44
diff -C2 -d -r1.43 -r1.44
*** dialog_map.py 22 Dec 2004 18:55:54 -0000 1.43
--- dialog_map.py 23 Dec 2004 01:42:38 -0000 1.44
***************
*** 33,37 ****
date_string = strftime("%a, %d %b %Y %I:%M:%S %p", reset_date)
else:
! date_string = "None"
win32gui.SendMessage(date_label, win32con.WM_SETTEXT, 0, date_string)
--- 33,37 ----
date_string = strftime("%a, %d %b %Y %I:%M:%S %p", reset_date)
else:
! date_string = "Never"
win32gui.SendMessage(date_label, win32con.WM_SETTEXT, 0, date_string)
From anadelonbrin at users.sourceforge.net Thu Dec 23 03:06:27 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 03:06:30 2004
Subject: [Spambayes-checkins] spambayes/spambayes message.py,1.65,1.66
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15825/spambayes
Modified Files:
message.py
Log Message:
Provide constants for the persistent ham/spam/unsure stings for the messageinfo db.
Index: message.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v
retrieving revision 1.65
retrieving revision 1.66
diff -C2 -d -r1.65 -r1.66
*** message.py 22 Dec 2004 18:37:36 -0000 1.65
--- message.py 23 Dec 2004 02:06:21 -0000 1.66
***************
*** 113,116 ****
--- 113,119 ----
STATS_START_KEY = "Statistics start date"
+ PERSISTENT_HAM_STRING = 'h'
+ PERSISTENT_SPAM_STRING = 's'
+ PERSISTENT_UNSURE_STRING = 'u'
class MessageInfoBase(object):
***************
*** 372,380 ****
def GetClassification(self):
! if self.c == 's':
return options['Headers','header_spam_string']
! elif self.c == 'h':
return options['Headers','header_ham_string']
! elif self.c == 'u':
return options['Headers','header_unsure_string']
return None
--- 375,383 ----
def GetClassification(self):
! if self.c == PERSISTENT_SPAM_STRING:
return options['Headers','header_spam_string']
! elif self.c == PERSISTENT_HAM_STRING:
return options['Headers','header_ham_string']
! elif self.c == PERSISTENT_UNSURE_STRING:
return options['Headers','header_unsure_string']
return None
***************
*** 385,393 ****
if cls == options['Headers','header_spam_string']:
! self.c = 's'
elif cls == options['Headers','header_ham_string']:
! self.c = 'h'
elif cls == options['Headers','header_unsure_string']:
! self.c = 'u'
else:
raise ValueError, \
--- 388,396 ----
if cls == options['Headers','header_spam_string']:
! self.c = PERSISTENT_SPAM_STRING
elif cls == options['Headers','header_ham_string']:
! self.c = PERSISTENT_HAM_STRING
elif cls == options['Headers','header_unsure_string']:
! self.c = PERSISTENT_UNSURE_STRING
else:
raise ValueError, \
From anadelonbrin at users.sourceforge.net Thu Dec 23 05:33:00 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 05:33:02 2004
Subject: [Spambayes-checkins] spambayes/spambayes storage.py,1.45,1.46
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15585/spambayes
Modified Files:
storage.py
Log Message:
Remove unused import.
Index: storage.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/storage.py,v
retrieving revision 1.45
retrieving revision 1.46
diff -C2 -d -r1.45 -r1.46
*** storage.py 8 Dec 2004 02:06:25 -0000 1.45
--- storage.py 23 Dec 2004 04:32:57 -0000 1.46
***************
*** 67,71 ****
import os
import sys
- import types
from spambayes import classifier
from spambayes.Options import options, get_pathname_option
--- 67,70 ----
From anadelonbrin at users.sourceforge.net Thu Dec 23 05:34:15 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 05:34:19 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.146,1.147
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15970/Outlook2000
Modified Files:
addin.py
Log Message:
Fix bug with last checkin.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.146
retrieving revision 1.147
diff -C2 -d -r1.146 -r1.147
*** addin.py 23 Dec 2004 01:48:26 -0000 1.146
--- addin.py 23 Dec 2004 04:34:13 -0000 1.147
***************
*** 769,773 ****
self.manager.score(msgstore_message))
msgstore_message.t = False
! self.manager.classifier_data.message_db.store_msg(msg)
self.manager.classifier_data.dirty = True
# Must train before moving, else we lose the message!
--- 769,773 ----
self.manager.score(msgstore_message))
msgstore_message.t = False
! self.manager.classifier_data.message_db.store_msg(msgstore_message)
self.manager.classifier_data.dirty = True
# Must train before moving, else we lose the message!
From anadelonbrin at users.sourceforge.net Thu Dec 23 06:18:24 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 06:18:27 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs/resources
dialogs.h, 1.23, 1.24 dialogs.rc, 1.49, 1.50
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv24915/Outlook2000/dialogs/resources
Modified Files:
dialogs.h dialogs.rc
Log Message:
Enlarge the Manager dialog. This makes room for all the statistics that we want to
show (plus one or two more later), and a control to set the ham folder. The general
tab has more room to show the folders being filtered (it never fit well before).
There's room on the training tab for some sort of option controlling the training
regime, and room on the advanced tab for something new, too.
Is this too large now? I don't think we'd want to go larger than this.
Index: dialogs.h
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/dialogs.h,v
retrieving revision 1.23
retrieving revision 1.24
diff -C2 -d -r1.23 -r1.24
*** dialogs.h 22 Dec 2004 18:55:54 -0000 1.23
--- dialogs.h 23 Dec 2004 05:18:21 -0000 1.24
***************
*** 56,63 ****
--- 56,66 ----
#define IDC_EDIT_UNSURE 1030
#define IDC_ACTION_UNSURE 1031
+ #define IDC_ACTION_HAM 1032
#define IDC_FOLDER_UNSURE 1033
#define IDC_BROWSE_UNSURE 1034
#define IDC_TRAINING_STATUS 1035
+ #define IDC_FOLDER_HAM 1098
#define IDC_FOLDER_NAMES 1036
+ #define IDC_BROWSE_HAM 1099
#define IDC_BROWSE 1037
#define IDC_FOLDER_WATCH 1038
Index: dialogs.rc
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/dialogs.rc,v
retrieving revision 1.49
retrieving revision 1.50
diff -C2 -d -r1.49 -r1.50
*** dialogs.rc 22 Dec 2004 18:55:55 -0000 1.49
--- dialogs.rc 23 Dec 2004 05:18:21 -0000 1.50
***************
*** 28,36 ****
//
! IDD_ADVANCED DIALOGEX 0, 0, 248, 209
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Advanced"
! FONT 8, "Tahoma", 400
BEGIN
GROUPBOX "Filter timer",IDC_STATIC,7,3,234,117
--- 28,36 ----
//
! IDD_ADVANCED DIALOGEX 0, 0, 248, 257
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Advanced"
! FONT 8, "Tahoma", 400, 0, 0x1
BEGIN
GROUPBOX "Filter timer",IDC_STATIC,7,3,234,117
***************
*** 48,80 ****
IDC_INBOX_TIMER_ONLY,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,16,100,217,10
! PUSHBUTTON "Show Data Folder",IDC_SHOW_DATA_FOLDER,7,190,70,14
CONTROL "Enable background filtering",IDC_BUT_TIMER_ENABLED,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,12,162,10
! PUSHBUTTON "Diagnostics...",IDC_BUT_SHOW_DIAGNOSTICS,171,190,70,14
END
! IDD_STATISTICS DIALOG DISCARDABLE 0, 0, 248, 209
STYLE WS_CHILD | WS_CAPTION
CAPTION "Statistics"
FONT 8, "Tahoma"
BEGIN
! GROUPBOX "Statistics",IDC_STATIC,7,3,241,181
LTEXT "some stats\nand some more\nline 3\nline 4\nline 5",
! IDC_STATISTICS,12,12,230,166
! PUSHBUTTON "Reset Statistics",IDC_BUT_RESET_STATS,178,190,70,14
! LTEXT "Last reset:",IDC_STATIC,7,193,36,8
! LTEXT "<<>>",IDC_LAST_RESET_DATE,47,193,107,8
END
! IDD_MANAGER DIALOGEX 0, 0, 275, 260
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "SpamBayes Manager"
! FONT 8, "Tahoma", 400
BEGIN
! DEFPUSHBUTTON "Close",IDOK,216,239,50,14
! PUSHBUTTON "Cancel",IDCANCEL,155,239,50,14,NOT WS_VISIBLE
! CONTROL "",IDC_TAB,"SysTabControl32",0x0,8,7,258,228
! PUSHBUTTON "About",IDC_ABOUT_BTN,8,239,50,14
END
--- 48,80 ----
IDC_INBOX_TIMER_ONLY,"Button",BS_AUTOCHECKBOX |
WS_TABSTOP,16,100,217,10
! PUSHBUTTON "Show Data Folder",IDC_SHOW_DATA_FOLDER,7,238,70,14
CONTROL "Enable background filtering",IDC_BUT_TIMER_ENABLED,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,12,162,10
! PUSHBUTTON "Diagnostics...",IDC_BUT_SHOW_DIAGNOSTICS,171,238,70,14
END
! IDD_STATISTICS DIALOG DISCARDABLE 0, 0, 248, 257
STYLE WS_CHILD | WS_CAPTION
CAPTION "Statistics"
FONT 8, "Tahoma"
BEGIN
! GROUPBOX "Statistics",IDC_STATIC,7,3,241,229
LTEXT "some stats\nand some more\nline 3\nline 4\nline 5",
! IDC_STATISTICS,12,12,230,204
! PUSHBUTTON "Reset Statistics",IDC_BUT_RESET_STATS,178,238,70,14
! LTEXT "Last reset:",IDC_STATIC,7,241,36,8
! LTEXT "<<>>",IDC_LAST_RESET_DATE,47,241,107,8
END
! IDD_MANAGER DIALOGEX 0, 0, 275, 308
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "SpamBayes Manager"
! FONT 8, "Tahoma"
BEGIN
! DEFPUSHBUTTON "Close",IDOK,216,287,50,14
! PUSHBUTTON "Cancel",IDCANCEL,155,287,50,14,NOT WS_VISIBLE
! CONTROL "",IDC_TAB,"SysTabControl32",0x0,8,7,258,276
! PUSHBUTTON "About",IDC_ABOUT_BTN,8,287,50,14
END
***************
*** 82,86 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Spam"
! FONT 8, "Tahoma", 400
BEGIN
LTEXT "Filter the following folders as messages arrive",
--- 82,86 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Spam"
! FONT 8, "Tahoma", 400, 0, 0x1
BEGIN
LTEXT "Filter the following folders as messages arrive",
***************
*** 111,115 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Possible Spam"
! FONT 8, "Tahoma", 400
BEGIN
LTEXT "To be considered uncertain, a message must score at least",
--- 111,115 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Possible Spam"
! FONT 8, "Tahoma", 400, 0, 0x1
BEGIN
LTEXT "To be considered uncertain, a message must score at least",
***************
*** 134,138 ****
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Diagnostics"
! FONT 8, "Tahoma", 400
BEGIN
LTEXT "These advanced options are for diagnostic or debugging purposes only. You should only change these options if specifically asked to, or you know exactly what they mean.",
--- 134,138 ----
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Diagnostics"
! FONT 8, "Tahoma", 400, 0, 0x1
BEGIN
LTEXT "These advanced options are for diagnostic or debugging purposes only. You should only change these options if specifically asked to, or you know exactly what they mean.",
***************
*** 151,155 ****
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "SpamBayes Configuration Wizard"
! FONT 8, "Tahoma", 400
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,328,173,50,14
--- 151,155 ----
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "SpamBayes Configuration Wizard"
! FONT 8, "Tahoma", 400, 0, 0x1
BEGIN
PUSHBUTTON "Cancel",IDCANCEL,328,173,50,14
***************
*** 164,168 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Welcome to the SpamBayes configuration wizard",
--- 164,168 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "Welcome to the SpamBayes configuration wizard",
***************
*** 189,193 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
--- 189,193 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
***************
*** 207,211 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_SPAM,208,85,60,15
--- 207,211 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_SPAM,208,85,60,15
***************
*** 227,231 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_WATCH,225,134,50,14
--- 227,231 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_WATCH,225,134,50,14
***************
*** 245,249 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Configuration cancelled",IDC_STATIC,20,4,247,14
--- 245,249 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "Configuration cancelled",IDC_STATIC,20,4,247,14
***************
*** 257,261 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_HAM,208,49,60,15
--- 257,261 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
PUSHBUTTON "Browse...",IDC_BROWSE_HAM,208,49,60,15
***************
*** 281,285 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Training",-1,20,4,247,14
--- 281,285 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "Training",-1,20,4,247,14
***************
*** 294,298 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
--- 294,298 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "Congratulations",IDC_STATIC,20,4,247,14
***************
*** 308,312 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "SpamBayes will not be effective until it is trained.",
--- 308,312 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "SpamBayes will not be effective until it is trained.",
***************
*** 331,335 ****
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma"
BEGIN
LTEXT "Configuration suspended",IDC_STATIC,20,4,247,14
--- 331,335 ----
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION
EXSTYLE WS_EX_CONTEXTHELP
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "Configuration suspended",IDC_STATIC,20,4,247,14
***************
*** 495,499 ****
//
! IDD_GENERAL DIALOGEX 0, 0, 253, 210
STYLE DS_MODALFRAME | WS_CHILD | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
--- 495,499 ----
//
! IDD_GENERAL DIALOGEX 0, 0, 253, 257
STYLE DS_MODALFRAME | WS_CHILD | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
***************
*** 508,516 ****
IDC_TRAINING_STATUS,6,101,242,27,SS_SUNKEN
CONTROL "Enable SpamBayes",IDC_BUT_FILTER_ENABLE,"Button",
! BS_AUTOCHECKBOX | WS_TABSTOP,6,173,97,11
LTEXT "Certain spam is moved to Folder1\nPossible spam is moved too",
! IDC_FILTER_STATUS,6,146,242,19,SS_SUNKEN
! PUSHBUTTON "Reset Configuration...",IDC_BUT_RESET,6,190,84,15
! PUSHBUTTON "Configuration Wizard...",IDC_BUT_WIZARD,164,190,84,15
LTEXT "Filter status:",IDC_STATIC,6,135,222,8
CONTROL 1062,IDC_LOGO_GRAPHIC,"Static",SS_BITMAP |
--- 508,516 ----
IDC_TRAINING_STATUS,6,101,242,27,SS_SUNKEN
CONTROL "Enable SpamBayes",IDC_BUT_FILTER_ENABLE,"Button",
! BS_AUTOCHECKBOX | WS_TABSTOP,6,221,97,11
LTEXT "Certain spam is moved to Folder1\nPossible spam is moved too",
! IDC_FILTER_STATUS,6,146,242,67,SS_SUNKEN
! PUSHBUTTON "Reset Configuration...",IDC_BUT_RESET,6,238,84,15
! PUSHBUTTON "Configuration Wizard...",IDC_BUT_WIZARD,164,238,84,15
LTEXT "Filter status:",IDC_STATIC,6,135,222,8
CONTROL 1062,IDC_LOGO_GRAPHIC,"Static",SS_BITMAP |
***************
*** 518,522 ****
END
! IDD_TRAINING DIALOGEX 0, 0, 252, 210
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
--- 518,522 ----
END
! IDD_TRAINING DIALOGEX 0, 0, 252, 257
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
***************
*** 589,597 ****
END
! IDD_FILTER DIALOGEX 0, 0, 249, 209
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Filtering"
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "Filter the following folders as messages arrive",
--- 589,597 ----
END
! IDD_FILTER DIALOGEX 0, 0, 249, 257
STYLE DS_MODALFRAME | WS_CHILD | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CONTEXTHELP
CAPTION "Filtering"
! FONT 8, "Tahoma"
BEGIN
LTEXT "Filter the following folders as messages arrive",
***************
*** 628,636 ****
SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | SS_SUNKEN |
WS_GROUP,102,166,77,14
! PUSHBUTTON "&Browse",IDC_BROWSE_UNSURE,183,166,50,14
CONTROL "Mark spam as read",IDC_MARK_SPAM_AS_READ,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,13,100,81,10
CONTROL "Mark possible spam as read",IDC_MARK_UNSURE_AS_READ,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,186,101,10
END
--- 628,645 ----
SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | SS_SUNKEN |
WS_GROUP,102,166,77,14
! PUSHBUTTON "&Browse",IDC_BROWSE_UNSURE,184,166,50,14
CONTROL "Mark spam as read",IDC_MARK_SPAM_AS_READ,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,13,100,81,10
CONTROL "Mark possible spam as read",IDC_MARK_UNSURE_AS_READ,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,186,101,10
+ GROUPBOX "Certain Good",IDC_STATIC,6,203,235,48
+ LTEXT "These messages should be:",IDC_STATIC,12,215,107,10
+ COMBOBOX IDC_ACTION_HAM,12,228,55,40,CBS_DROPDOWNLIST |
+ WS_VSCROLL | WS_TABSTOP
+ LTEXT "to folder",IDC_STATIC,71,230,27,10
+ CONTROL "(folder name)",IDC_FOLDER_HAM,"Static",
+ SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | SS_SUNKEN |
+ WS_GROUP,102,228,77,14
+ PUSHBUTTON "&Browse",IDC_BROWSE_HAM,184,228,50,14
END
***************
*** 638,642 ****
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
! FONT 8, "Tahoma"
BEGIN
LTEXT "&Folders:",IDC_STATIC,7,7,47,9
--- 647,651 ----
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
! FONT 8, "Tahoma", 0, 0, 0x1
BEGIN
LTEXT "&Folders:",IDC_STATIC,7,7,47,9
***************
*** 689,693 ****
IDD_FILTER, DIALOG
BEGIN
! BOTTOMMARGIN, 206
HORZGUIDE, 127
END
--- 698,702 ----
IDD_FILTER, DIALOG
BEGIN
! BOTTOMMARGIN, 254
HORZGUIDE, 127
END
From anadelonbrin at users.sourceforge.net Thu Dec 23 06:19:42 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Thu Dec 23 06:19:45 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs dialog_map.py,
1.44, 1.45
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv25096/Outlook2000/dialogs
Modified Files:
dialog_map.py
Log Message:
Attach the ham folder options.
Index: dialog_map.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dialog_map.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -C2 -d -r1.44 -r1.45
*** dialog_map.py 23 Dec 2004 01:42:38 -0000 1.44
--- dialog_map.py 23 Dec 2004 05:19:39 -0000 1.45
***************
*** 483,489 ****
(EditNumberProcessor, "IDC_EDIT_UNSURE IDC_SLIDER_UNSURE",
"Filter.unsure_threshold"),
-
(ComboProcessor, "IDC_ACTION_UNSURE", "Filter.unsure_action"),
(BoolButtonProcessor, "IDC_MARK_UNSURE_AS_READ", "Filter.unsure_mark_as_read"),
),
"IDD_TRAINING" : (
--- 483,491 ----
(EditNumberProcessor, "IDC_EDIT_UNSURE IDC_SLIDER_UNSURE",
"Filter.unsure_threshold"),
(ComboProcessor, "IDC_ACTION_UNSURE", "Filter.unsure_action"),
(BoolButtonProcessor, "IDC_MARK_UNSURE_AS_READ", "Filter.unsure_mark_as_read"),
+ (FolderIDProcessor, "IDC_FOLDER_HAM IDC_BROWSE_HAM",
+ "Filter.ham_folder_id"),
+ (ComboProcessor, "IDC_ACTION_HAM", "Filter.ham_action"),
),
"IDD_TRAINING" : (
From kpitt at users.sourceforge.net Thu Dec 23 18:13:40 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 23 18:13:43 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs/resources
dialogs.h, 1.24, 1.25
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19932
Modified Files:
dialogs.h
Log Message:
Update the _APS_NEXT_CONTROL_VALUE so that we don't get an id conflict the
next time someone adds a control from the Visual Studio resource editor.
Index: dialogs.h
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/dialogs.h,v
retrieving revision 1.24
retrieving revision 1.25
diff -C2 -d -r1.24 -r1.25
*** dialogs.h 23 Dec 2004 05:18:21 -0000 1.24
--- dialogs.h 23 Dec 2004 17:13:37 -0000 1.25
***************
*** 112,116 ****
#define _APS_NEXT_RESOURCE_VALUE 128
#define _APS_NEXT_COMMAND_VALUE 40001
! #define _APS_NEXT_CONTROL_VALUE 1098
#define _APS_NEXT_SYMED_VALUE 101
#endif
--- 112,116 ----
#define _APS_NEXT_RESOURCE_VALUE 128
#define _APS_NEXT_COMMAND_VALUE 40001
! #define _APS_NEXT_CONTROL_VALUE 1100
#define _APS_NEXT_SYMED_VALUE 101
#endif
From kpitt at users.sourceforge.net Thu Dec 23 19:14:35 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 23 19:14:39 2004
Subject: [Spambayes-checkins] spambayes/scripts sb_filter.py, 1.15,
1.16 sb_imapfilter.py, 1.49, 1.50 sb_pop3dnd.py, 1.16,
1.17 sb_server.py, 1.37, 1.38
Message-ID:
Update of /cvsroot/spambayes/spambayes/scripts
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1047/scripts
Modified Files:
sb_filter.py sb_imapfilter.py sb_pop3dnd.py sb_server.py
Log Message:
New version numbering scheme. All apps now use the same version number, and
the current version information is read from the __init__.py file in the
"spambayes" module.
Float version numbers are no longer used for update checks. Instead, our
standard string version number format is parsed into a tuple in the format of
the sys.version_info field. Versions can then be compared based on that
tuple.
Index: sb_filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_filter.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -C2 -d -r1.15 -r1.16
*** sb_filter.py 22 Nov 2004 00:13:43 -0000 1.15
--- sb_filter.py 23 Dec 2004 18:14:18 -0000 1.16
***************
*** 77,81 ****
import getopt
from spambayes import hammie, Options, mboxutils, storage
! from spambayes.Version import get_version_string
try:
--- 77,81 ----
import getopt
from spambayes import hammie, Options, mboxutils, storage
! from spambayes.Version import get_current_version
try:
***************
*** 126,131 ****
"""Print usage message and sys.exit(code)."""
# Include version info in usage
! print >> sys.stderr, get_version_string("sb_filter")
! print >> sys.stderr, " with engine %s" % get_version_string()
print >> sys.stderr
--- 126,131 ----
"""Print usage message and sys.exit(code)."""
# Include version info in usage
! v = get_current_version()
! print >> sys.stderr, v.get_long_version("SpamBayes Command Line Filter")
print >> sys.stderr
Index: sb_imapfilter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v
retrieving revision 1.49
retrieving revision 1.50
diff -C2 -d -r1.49 -r1.50
*** sb_imapfilter.py 22 Dec 2004 00:27:17 -0000 1.49
--- sb_imapfilter.py 23 Dec 2004 18:14:32 -0000 1.50
***************
*** 104,108 ****
from spambayes.UserInterface import UserInterfaceServer
from spambayes.ImapUI import IMAPUserInterface
! from spambayes.Version import get_version_string
from imaplib import IMAP4
--- 104,108 ----
from spambayes.UserInterface import UserInterfaceServer
from spambayes.ImapUI import IMAPUserInterface
! from spambayes.Version import get_current_version
from imaplib import IMAP4
***************
*** 1023,1028 ****
# Let the user know what they are using...
! print get_version_string("IMAP Filter")
! print "and engine %s.\n" % (get_version_string(),)
if options["globals", "verbose"]:
--- 1023,1028 ----
# Let the user know what they are using...
! v = get_current_version();
! print "%s.\n" % (v.get_long_version("SpamBayes IMAP Filter"),)
if options["globals", "verbose"]:
Index: sb_pop3dnd.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_pop3dnd.py,v
retrieving revision 1.16
retrieving revision 1.17
diff -C2 -d -r1.16 -r1.17
*** sb_pop3dnd.py 22 Dec 2004 01:51:49 -0000 1.16
--- sb_pop3dnd.py 23 Dec 2004 18:14:32 -0000 1.17
***************
*** 101,105 ****
from spambayes.tokenizer import tokenize
from spambayes import FileCorpus, Dibbler
! from spambayes.Version import get_version_string
from sb_server import POP3ProxyBase, State, _addressPortStr, _recreateState
--- 101,105 ----
from spambayes.tokenizer import tokenize
from spambayes import FileCorpus, Dibbler
! from spambayes.Version import get_current_version
from sb_server import POP3ProxyBase, State, _addressPortStr, _recreateState
***************
*** 1021,1027 ****
# Let the user know what they are using...
! print get_version_string("IMAP Server")
! print get_version_string("POP3 Proxy")
! print get_version_string()
from twisted.copyright import version as twisted_version
print "Twisted version %s.\n" % (twisted_version,)
--- 1021,1026 ----
# Let the user know what they are using...
! v = get_current_version()
! print v.get_long_version()
from twisted.copyright import version as twisted_version
print "Twisted version %s.\n" % (twisted_version,)
Index: sb_server.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v
retrieving revision 1.37
retrieving revision 1.38
diff -C2 -d -r1.37 -r1.38
*** sb_server.py 22 Dec 2004 01:51:49 -0000 1.37
--- sb_server.py 23 Dec 2004 18:14:32 -0000 1.38
***************
*** 119,123 ****
from spambayes.UserInterface import UserInterfaceServer
from spambayes.ProxyUI import ProxyUserInterface
! from spambayes.Version import get_version_string
--- 119,123 ----
from spambayes.UserInterface import UserInterfaceServer
from spambayes.ProxyUI import ProxyUserInterface
! from spambayes.Version import get_current_version
***************
*** 990,995 ****
# Let the user know what they are using...
! print get_version_string("POP3 Proxy")
! print "and engine %s.\n" % (get_version_string(),)
if 0 <= len(args) <= 2:
--- 990,995 ----
# Let the user know what they are using...
! v = get_current_version()
! print "%s\n" % (v.get_long_version("SpamBayes POP3 Proxy"),)
if 0 <= len(args) <= 2:
From kpitt at users.sourceforge.net Thu Dec 23 19:14:37 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 23 19:14:40 2004
Subject: [Spambayes-checkins] spambayes/windows pop3proxy_tray.py,1.22,1.23
Message-ID:
Update of /cvsroot/spambayes/spambayes/windows
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1047/windows
Modified Files:
pop3proxy_tray.py
Log Message:
New version numbering scheme. All apps now use the same version number, and
the current version information is read from the __init__.py file in the
"spambayes" module.
Float version numbers are no longer used for update checks. Instead, our
standard string version number format is parsed into a tuple in the format of
the sys.version_info field. Versions can then be compared based on that
tuple.
Index: pop3proxy_tray.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_tray.py,v
retrieving revision 1.22
retrieving revision 1.23
diff -C2 -d -r1.22 -r1.23
*** pop3proxy_tray.py 22 Jul 2004 05:56:56 -0000 1.22
--- pop3proxy_tray.py 23 Dec 2004 18:14:33 -0000 1.23
***************
*** 507,522 ****
def CheckVersion(self):
# Stolen, with few modifications, from addin.py
! from spambayes.Version import get_version_string, \
! get_version_number, fetch_latest_dict
! if hasattr(sys, "frozen"):
! version_number_key = "BinaryVersion"
! version_string_key = "Full Description Binary"
! else:
! version_number_key = "Version"
! version_string_key = "Full Description"
app_name = "POP3 Proxy"
! cur_ver_string = get_version_string(app_name, version_string_key)
! cur_ver_num = get_version_number(app_name, version_number_key)
try:
--- 507,517 ----
def CheckVersion(self):
# Stolen, with few modifications, from addin.py
! from spambayes.Version import get_current_version, get_version, \
! get_download_page, fetch_latest_dict
app_name = "POP3 Proxy"
! app_display_name = "SpamBayes POP3 Proxy"
! ver_current = get_current_version()
! cur_ver_string = ver_current.get_long_version(app_display_name)
try:
***************
*** 524,536 ****
latest = fetch_latest_dict()
SetWaitCursor(0)
! try:
! latest_ver_string = get_version_string(app_name, version_string_key,
! version_dict=latest)
! latest_ver_num = get_version_number(app_name, version_number_key,
! version_dict=latest)
! except KeyError:
! # "Full Description Binary" not in the version currently on the web
! latest_ver_string = "0.1"
! latest_ver_num = 0.1
except:
self.ShowMessage("Error checking the latest version")
--- 519,524 ----
latest = fetch_latest_dict()
SetWaitCursor(0)
! ver_latest = get_version(app_name, version_dict=latest)
! latest_ver_string = ver_latest.get_long_version(app_display_name)
except:
self.ShowMessage("Error checking the latest version")
***************
*** 538,547 ****
return
! self.ShowMessage("Current version is %s, latest is %s." % \
! (cur_ver_string, latest_ver_string))
! if latest_ver_num > cur_ver_num:
! url = get_version_string(app_name, "Download Page", version_dict=latest)
! # Offer to open up the url
! ## os.startfile(url)
def ShowMessage(self, msg):
--- 526,546 ----
return
! ver_message = "Current version is %s.\r\n" \
! "Latest version is %s.\r\n\r\n" % \
! (cur_ver_string, latest_ver_string)
! if ver_latest == ver_current:
! ver_message += "Your are running the latest downloadable version."
! elif ver_current > ver_latest:
! ver_message += "Your current version is newer than the latest " \
! "downloadable version."
! else:
! ver_message += "There is a newer version available. You may " \
! "download the updated version from:\r\n"
! url = get_download_page(app_name, version_dict=latest)
! ver_message += url
! self.ShowMessage(ver_message)
! # It would be nice to offer to open up the url if there is a
! # newer version, but we don't do that yet.
! ## os.startfile(url)
def ShowMessage(self, msg):
From kpitt at users.sourceforge.net Thu Dec 23 19:14:38 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 23 19:14:44 2004
Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py, 1.41,
1.42 ProxyUI.py, 1.56, 1.57 UserInterface.py, 1.51,
1.52 Version.py, 1.33, 1.34 __init__.py, 1.13, 1.14
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1047/spambayes
Modified Files:
ImapUI.py ProxyUI.py UserInterface.py Version.py __init__.py
Log Message:
New version numbering scheme. All apps now use the same version number, and
the current version information is read from the __init__.py file in the
"spambayes" module.
Float version numbers are no longer used for update checks. Instead, our
standard string version number format is parsed into a tuple in the format of
the sys.version_info field. Versions can then be compared based on that
tuple.
Index: ImapUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v
retrieving revision 1.41
retrieving revision 1.42
diff -C2 -d -r1.41 -r1.42
*** ImapUI.py 22 Dec 2004 00:23:58 -0000 1.41
--- ImapUI.py 23 Dec 2004 18:14:32 -0000 1.42
***************
*** 128,132 ****
self.imap_pwd = pwd
self.imap_logged_in = False
! self.app_for_version = "IMAP Filter"
self.imap_session_class = imap_session_class
--- 128,132 ----
self.imap_pwd = pwd
self.imap_logged_in = False
! self.app_for_version = "SpamBayes IMAP Filter"
self.imap_session_class = imap_session_class
Index: ProxyUI.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v
retrieving revision 1.56
retrieving revision 1.57
diff -C2 -d -r1.56 -r1.57
*** ProxyUI.py 22 Dec 2004 00:23:58 -0000 1.56
--- ProxyUI.py 23 Dec 2004 18:14:32 -0000 1.57
***************
*** 169,173 ****
state = proxy_state
self.state_recreator = state_recreator # ugly
! self.app_for_version = "POP3 Proxy"
self.previous_sort = None
if not proxy_state.can_stop:
--- 169,173 ----
state = proxy_state
self.state_recreator = state_recreator # ugly
! self.app_for_version = "SpamBayes POP3 Proxy"
self.previous_sort = None
if not proxy_state.can_stop:
Index: UserInterface.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v
retrieving revision 1.51
retrieving revision 1.52
diff -C2 -d -r1.51 -r1.52
*** UserInterface.py 22 Dec 2004 00:23:58 -0000 1.51
--- UserInterface.py 23 Dec 2004 18:14:32 -0000 1.52
***************
*** 162,166 ****
timestamp = time.strftime('%H:%M on %A %B %d %Y', time.localtime())
clone.footer.timestamp = timestamp
! clone.footer.version = Version.get_version_string(self.app_for_version)
if help_topic:
clone.helplink.href = "help?topic=%s" % (help_topic,)
--- 162,167 ----
timestamp = time.strftime('%H:%M on %A %B %d %Y', time.localtime())
clone.footer.timestamp = timestamp
! v = Version.get_current_version()
! clone.footer.version = v.get_long_version(self.app_for_version)
if help_topic:
clone.helplink.href = "help?topic=%s" % (help_topic,)
***************
*** 940,944 ****
report = self.html.bugreport.clone()
# Prefill the report
! sb_ver = Version.get_version_string(self.app_for_version)
if hasattr(sys, "frozen"):
sb_type = "binary"
--- 941,946 ----
report = self.html.bugreport.clone()
# Prefill the report
! v = Version.get_current_version()
! sb_ver = v.get_long_version(self.app_for_version)
if hasattr(sys, "frozen"):
sb_type = "binary"
***************
*** 1042,1046 ****
outer['CC'] = from_addr
outer['From'] = from_addr
! outer['X-Mailer'] = Version.get_version_string(self.app_for_version)
outer.preamble = self._wrap(message)
# To guarantee the message ends with a newline
--- 1044,1049 ----
outer['CC'] = from_addr
outer['From'] = from_addr
! v = Version.get_current_version()
! outer['X-Mailer'] = v.get_long_version(self.app_for_version)
outer.preamble = self._wrap(message)
# To guarantee the message ends with a newline
Index: Version.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Version.py,v
retrieving revision 1.33
retrieving revision 1.34
diff -C2 -d -r1.33 -r1.34
*** Version.py 22 Jul 2004 23:25:43 -0000 1.33
--- Version.py 23 Dec 2004 18:14:33 -0000 1.34
***************
*** 11,14 ****
--- 11,22 ----
"""
+ import string, re
+ from types import StringType
+
+ try:
+ _
+ except NameError:
+ _ = lambda arg: arg
+
# See bug 806238: urllib2 fails in Outlook new-version chk.
# A reason for why the spambayes.org URL fails is given in a comment there.
***************
*** 16,116 ****
# The SF URL instead works for Tim and xenogeist.
LATEST_VERSION_HOME="http://spambayes.sourceforge.net/download/Version.cfg"
! # This module is part of the spambayes project, which is Copyright 2002-4
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
versions = {
! # Non app specific - changed when "spambayes\*" changes significantly
! "Version": 0.3,
! "Description": "SpamBayes Engine",
! "Date": "January 2004",
! "Full Description": "%(Description)s Version %(Version)s (%(Date)s)",
! # Sub-dict for application specific version strings.
"Apps": {
- "sb_filter" : {
- "Version": 0.3,
- "Description": "SpamBayes Command Line Filter",
- "Date": "April 2004",
- "Full Description": "%(Description)s Version %(Version)s (%(Date)s)",
- },
"Outlook" : {
- # Note these version numbers currently don't appear in the
- # "description" strings below - they just need to increment
- # so automated version checking works.
- # 0.99 indicates '1.0b/rc/' so will go 0.992 etc, until a real
- # 1.0, which can get 1.0 :)
- "Version": 0.992,
- "BinaryVersion": 0.992,
"Description": "SpamBayes Outlook Addin",
- "Date": "May 2004",
- "Full Description": "%(Description)s Version 1.0rc1 (%(Date)s)",
- "Full Description Binary":
- "%(Description)s Binary Version 1.0rc1 (%(Date)s)",
- # Note this means we can change the download page later, and old
- # versions will still go to the new page.
- # We may also like to have a "Release Notes Page" item later?
- "Download Page": "http://spambayes.sourceforge.net/windows.html"
},
"POP3 Proxy" : {
- # Note these version numbers also currently don't appear in the
- # "description" strings below - see above
- "Version": 0.6,
- "BinaryVersion": 0.6,
"Description": "SpamBayes POP3 Proxy",
- "Date": "May 2004",
- "Full Description": """%(Description)s Version 1.0rc1 (%(Date)s)""",
- "Full Description Binary":
- """%(Description)s Binary Version 1.0rc1 (%(Date)s)""",
- # Note this means we can change the download page later, and old
- # versions will still go to the new page.
- # We may also like to have a "Release Notes Page" item later?
- "Download Page": "http://spambayes.sourceforge.net/windows.html"
- },
- "Lotus Notes Filter" : {
- "Version": 0.02,
- "Description": "SpamBayes Lotus Notes Filter",
- "Date": "February 2004",
- "Full Description": "%(Description)s Version %(Version)s (%(Date)s)",
- },
- "IMAP Filter" : {
- "Version": 0.4,
- "Description": "SpamBayes IMAP Filter",
- "Date": "May 2004",
- "Full Description": """%(Description)s Version %(Version)s (%(Date)s)""",
- },
- "IMAP Server" : {
- "Version": 0.02,
- "Description": "SpamBayes IMAP Server",
- "Date": "January 2004",
- "Full Description": """%(Description)s Version %(Version)s (%(Date)s)""",
},
},
}
! def get_version_string(app = None,
! description_key = "Full Description",
! version_dict = None):
! """Get a pretty version string, generally just to log or show in a UI"""
if version_dict is None: version_dict = versions
! if app is None:
! dict = version_dict
! else:
! dict = version_dict["Apps"][app]
! return dict[description_key] % dict
! def get_version_number(app = None,
! version_key = "Version",
! version_dict = None):
! """Get a version number, as a float. This would primarily be used so some
! app or extension can determine if we are later than a specific version
! of either the engine or a specific app.
! Maybe YAGNI.
"""
! if version_dict is None: version_dict = versions
! if app is None:
! dict = version_dict
! else:
! dict = version_dict["Apps"][app]
! return dict[version_key]
# Utilities to check the "latest" version of an app.
--- 24,228 ----
# The SF URL instead works for Tim and xenogeist.
LATEST_VERSION_HOME="http://spambayes.sourceforge.net/download/Version.cfg"
+ DEFAULT_DOWNLOAD_PAGE="http://spambayes.sourceforge.net/windows.html"
! # This module is part of the spambayes project, which is Copyright 2002-5
# The Python Software Foundation and is covered by the Python Software
# Foundation license.
+
versions = {
! # Note this means we can change the download page later, and old
! # versions will still go to the new page.
! # We may also like to have a "Release Notes Page" item later?
! "Download Page": DEFAULT_DOWNLOAD_PAGE,
!
! # Sub-dict for generating sub-sections in the cfg file that are compatible
! # with the update checking in older versions of SpamBayes.
"Apps": {
"Outlook" : {
"Description": "SpamBayes Outlook Addin",
},
"POP3 Proxy" : {
"Description": "SpamBayes POP3 Proxy",
},
},
}
! def get_version(app = None,
! version_dict = None):
! """Get SBVersion object based on the version info in the supplied dict."""
! ver = SBVersion() # get default version
! if version_dict is not None:
! dict = version_dict # default to top level dictionary
! if app is not None:
! # attempt to get a sub-dict for the specific app
! try:
! dict = version_dict["Apps"][app]
! except KeyError:
! pass
! try:
! version = dict["Version"]
! # KLUDGE: Perform some bizarre magic to try to figure out if we
! # have an old-format float version instead of a new-format string
! # and massage it into a string format that will compare properly
! # in update checks.
! try:
! ver_num = float(version)
! # Version converted successfully to a float, which means it
! # may be an old-format version number. Old convention was to
! # use 1.01 to represent "1.0.1", so check to see if there is
! # more than one digit following the decimal.
! dot = version.find('.')
! ver_frac_part = version[dot+1:]
! if len(ver_frac_part) > 1:
! # Use the first digit of the fractional part as the minor
! # version and the rest as the patch version.
! version = version[0:dot] + '.' + ver_frac_part[0] + '.' + ver_frac_part[1:]
! except ValueError:
! pass
! ver = SBVersion(version, version_dict["Date"])
! except KeyError:
! pass
! return ver
!
! def get_download_page(app = None,
! version_dict = None):
if version_dict is None: version_dict = versions
! dict = version_dict # default to top level dictionary
! if app is not None:
! # attempt to get a sub-dict for the specific app
! try:
! dict = version_dict["Apps"][app]
! except KeyError:
! pass
! try:
! return dict["Download Page"]
! except KeyError:
! # "Download Page" key not found so it may be an old-format dictionary.
! # Just use the default download page.
! return DEFAULT_DOWNLOAD_PAGE
! def get_current_version():
! return SBVersion()
!
! #============================================================================
!
! # The SBVersion class is a modified version of the StrictVersion class from
! # the "distutils" module. It has been adapted to handle an "rc" pre-release
! # designation for release candidates, and to store the version data in the
! # format of the sys.version_info tuple. A date string may also be provided
! # that will be included in the long format of the version string. The
! # default version and date info is read from the metadata in the "spambayes"
! # module __init__.py file.
!
! class SBVersion:
!
! """Version numbering for SpamBayes releases.
! A version number consists of two or three dot-separated numeric
! components, with an optional "pre-release" tag on the end. The
! pre-release tag consists of the designations 'a' (for alpha),
! 'b' (for beta), or 'rc' (for release candidate) followed by a number.
! If the numeric components of two version numbers are equal, then one
! with a pre-release tag will always be deemed earlier (lesser) than
! one without.
!
! The following are valid version numbers (shown in the order that
! would be obtained by sorting according to the supplied cmp function):
!
! 0.4 0.4.0 (these two are equivalent)
! 0.4.1
! 0.5a1
! 0.5b3
! 0.5
! 0.9.6
! 1.0
! 1.0.4a3
! 1.0.4b1
! 1.0.4rc2
! 1.0.4
!
! The following are examples of invalid version numbers:
!
! 1
! 2.7.2.2
! 1.3.a4
! 1.3pl1
! 1.3c4
!
! A date may also be associated with the version, typically to track the
! date when the release was made public. The date is specified as a string,
! and is only used in formatting the long version of the version string.
"""
!
! def __init__(self, vstring=None, date=None):
! import spambayes
! if vstring:
! self.parse(vstring)
! else:
! self.parse(spambayes.__version__)
! if date:
! self.date = date
! else:
! self.date = spambayes.__date__
!
! def __repr__ (self):
! return "%s('%s', '%s')" % (self.__class__.__name__, str(self), self.date)
!
! version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? (([ab]|rc)(\d+))?$',
! re.VERBOSE)
!
! def parse(self, vstring):
! match = self.version_re.match(vstring)
! if not match:
! raise ValueError, "invalid version number '%s'" % vstring
!
! (major, minor, patch, prerelease, prerelease_num) = \
! match.group(1, 2, 4, 6, 7)
!
! if not patch:
! patch = "0"
!
! if not prerelease:
! releaselevel = "final"
! serial = 0
! else:
! serial = string.atoi(prerelease_num)
! if prerelease == "a":
! releaselevel = "alpha"
! elif prerelease == "b":
! releaselevel = "beta"
! elif prerelease == "rc":
! releaselevel = "candidate"
! self.version_info = tuple(map(string.atoi, [major, minor, patch]) + \
! [releaselevel, serial])
!
! def __str__(self):
! if self.version_info[2] == 0:
! vstring = string.join(map(str, self.version_info[0:2]), '.')
! else:
! vstring = string.join(map(str, self.version_info[0:3]), '.')
!
! releaselevel = self.version_info[3][0]
! if releaselevel != 'f':
! if releaselevel == 'a':
! prerelease = "a"
! elif releaselevel == 'b':
! prerelease = "b"
! elif releaselevel == 'c':
! prerelease = "rc"
! vstring = vstring + prerelease + str(self.version_info[4])
!
! return vstring
!
! def __cmp__(self, other):
! if isinstance(other, StringType):
! other = SBVersion(other)
!
! return cmp(self.version_info, other.version_info)
!
! def get_long_version(self, app_name = None):
! if app_name is None: app_name = "SpamBayes"
! return _("%s Version %s (%s)") % (app_name, str(self), self.date)
!
! #============================================================================
# Utilities to check the "latest" version of an app.
***************
*** 128,132 ****
if MySafeConfigParser is None:
raise RuntimeError, \
! "Sorry, but only Python 2.3 can trust remote config files"
import urllib2
--- 240,244 ----
if MySafeConfigParser is None:
raise RuntimeError, \
! "Sorry, but only Python 2.3+ can trust remote config files"
import urllib2
***************
*** 163,171 ****
for opt in cfg.options(sect):
val = cfg.get(sect, opt)
- # some butchering
- try:
- val = float(val)
- except ValueError:
- pass
target_dict[opt] = val
return ret_dict
--- 275,278 ----
***************
*** 173,178 ****
# Utilities for generating a 'config' version of this file.
# The output of this should exist at the URL above.
! def _make_cfg_section(stream, key, this_dict):
! stream.write("[%s]\n" % key)
for name, val in this_dict.items():
if type(val)==type(''):
--- 280,298 ----
# Utilities for generating a 'config' version of this file.
# The output of this should exist at the URL above.
! compat_apps = {
! "Outlook" : {
! "Description": "SpamBayes Outlook Addin",
! },
! "POP3 Proxy" : {
! "Description": "SpamBayes POP3 Proxy",
! },
! }
! shared_cfg_opts = {
! # Note this means we can change the download page later, and old
! # versions will still go to the new page.
! # We may also like to have a "Release Notes Page" item later?
! "Download Page": "http://spambayes.sourceforge.net/windows.html",
! }
! def _write_cfg_opts(stream, this_dict):
for name, val in this_dict.items():
if type(val)==type(''):
***************
*** 187,190 ****
--- 307,341 ----
if val_str is not None:
stream.write("%s:%s\n" % (name, val_str))
+ def _make_compatible_cfg_section(stream, key, ver, this_dict):
+ stream.write("[%s]\n" % key)
+ # We need to create a float representation of the current version that
+ # sort correctly in older versions that used a float version number.
+ ver_num = float(ver.version_info[0])
+ ver_num += float(ver.version_info[1] * 0.1)
+ ver_num += float(ver.version_info[2] * 0.01)
+ releaselevel = ver.version_info[3][0]
+ if releaselevel == 'a':
+ prerelease_offset = 0.001 - (float(ver.version_info[4]) * 0.00001)
+ elif releaselevel == 'b':
+ prerelease_offset = 0.0005 - (float(ver.version_info[4]) * 0.00001)
+ elif releaselevel == 'c':
+ prerelease_offset = 0.0001 - (float(ver.version_info[4]) * 0.00001)
+ else:
+ prerelease_offset = 0.0
+ ver_num -= prerelease_offset
+ stream.write("Version:%s\n" % str(ver_num))
+ stream.write("BinaryVersion:%s\n" % str(ver_num))
+ stream.write("Date:%s\n" % ver.date)
+ _write_cfg_opts(stream, this_dict)
+ desc_str = "%%(Description)s Version %s (%%(Date)s)" % str(ver)
+ stream.write("Full Description:%s\n" % desc_str)
+ stream.write("Full Description Binary:%s\n" % desc_str)
+ _write_cfg_opts(stream, versions)
+ stream.write("\n")
+ def _make_cfg_section(stream, ver):
+ stream.write("[SpamBayes]\n")
+ stream.write("Version:%s\n" % str(ver))
+ stream.write("Date:%s\n" % ver.date)
+ _write_cfg_opts(stream, versions)
stream.write("\n")
***************
*** 192,198 ****
stream.write("# This file is generated from spambayes/Version.py" \
" - do not edit\n")
! _make_cfg_section(stream, "SpamBayes", versions)
! for appname in versions["Apps"]:
! _make_cfg_section(stream, appname, versions["Apps"][appname])
def main(args):
--- 343,350 ----
stream.write("# This file is generated from spambayes/Version.py" \
" - do not edit\n")
! ver = get_current_version()
! _make_cfg_section(stream, ver)
! for appname in compat_apps:
! _make_compatible_cfg_section(stream, appname, ver, versions["Apps"][appname])
def main(args):
***************
*** 201,210 ****
make_cfg(sys.stdout)
sys.exit(0)
! print "SpamBayes engine version:", get_version_string()
! # Enumerate applications
! print
! print "Application versions:"
! for app in versions["Apps"]:
! print "\n%s: %s" % (app, get_version_string(app))
print
--- 353,359 ----
make_cfg(sys.stdout)
sys.exit(0)
!
! v_this = get_current_version()
! print "Current version:", v_this.get_long_version()
print
***************
*** 218,228 ****
sys.exit(1)
print
! print "SpamBayes engine version:", get_version_string(version_dict=latest_dict)
! # Enumerate applications
! print
! print "Application versions:"
! for app in latest_dict["Apps"]:
! print "\n%s: %s" % (app, get_version_string(app, version_dict=latest_dict))
if __name__=='__main__':
--- 367,373 ----
sys.exit(1)
+ v_latest = get_version(version_dict=latest_dict)
print
! print "Latest version:", v_latest.get_long_version()
if __name__=='__main__':
Index: __init__.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/__init__.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -d -r1.13 -r1.14
*** __init__.py 30 Nov 2004 21:49:19 -0000 1.13
--- __init__.py 23 Dec 2004 18:14:33 -0000 1.14
***************
*** 1,3 ****
# package marker.
! __version__ = '1.1a0'
--- 1,4 ----
# package marker.
! __version__ = "1.1a0"
! __date__ = "January 2005"
From kpitt at users.sourceforge.net Thu Dec 23 19:14:50 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 23 19:14:53 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs dialog_map.py,
1.45, 1.46
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1047/Outlook2000/dialogs
Modified Files:
dialog_map.py
Log Message:
New version numbering scheme. All apps now use the same version number, and
the current version information is read from the __init__.py file in the
"spambayes" module.
Float version numbers are no longer used for update checks. Instead, our
standard string version number format is parsed into a tuple in the format of
the sys.version_info field. Versions can then be compared based on that
tuple.
Index: dialog_map.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dialog_map.py,v
retrieving revision 1.45
retrieving revision 1.46
diff -C2 -d -r1.45 -r1.46
*** dialog_map.py 23 Dec 2004 05:19:39 -0000 1.45
--- dialog_map.py 23 Dec 2004 18:14:17 -0000 1.46
***************
*** 61,72 ****
class VersionStringProcessor(ControlProcessor):
def Init(self):
! from spambayes.Version import get_version_string
import sys
! version_key = "Full Description"
! if hasattr(sys, "frozen"):
! version_key += " Binary"
! version_string = get_version_string("Outlook", version_key)
! win32gui.SendMessage(self.GetControl(), win32con.WM_SETTEXT,
! 0, version_string)
def GetPopupHelpText(self, cid):
--- 61,71 ----
class VersionStringProcessor(ControlProcessor):
def Init(self):
! from spambayes.Version import get_current_version
import sys
! v = get_current_version()
! vstring = v.get_long_version("SpamBayes Outlook Addin")
! if not hasattr(sys, "frozen"):
! vstring += " from source"
! win32gui.SendMessage(self.GetControl(), win32con.WM_SETTEXT, 0, vstring)
def GetPopupHelpText(self, cid):
From kpitt at users.sourceforge.net Thu Dec 23 19:14:50 2004
From: kpitt at users.sourceforge.net (Kenny Pitt)
Date: Thu Dec 23 19:14:53 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.147,1.148
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1047/Outlook2000
Modified Files:
addin.py
Log Message:
New version numbering scheme. All apps now use the same version number, and
the current version information is read from the __init__.py file in the
"spambayes" module.
Float version numbers are no longer used for update checks. Instead, our
standard string version number format is parsed into a tuple in the format of
the sys.version_info field. Versions can then be compared based on that
tuple.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.147
retrieving revision 1.148
diff -C2 -d -r1.147 -r1.148
*** addin.py 23 Dec 2004 04:34:13 -0000 1.147
--- addin.py 23 Dec 2004 18:14:16 -0000 1.148
***************
*** 45,48 ****
--- 45,49 ----
toolbar_name = "SpamBayes"
+ ADDIN_DISPLAY_NAME = "SpamBayes Outlook Addin"
# If we are not running in a console, redirect all print statements to the
***************
*** 592,606 ****
def CheckLatestVersion(manager):
! from spambayes.Version import get_version_string, get_version_number, fetch_latest_dict
! if hasattr(sys, "frozen"):
! version_number_key = "BinaryVersion"
! version_string_key = "Full Description Binary"
! else:
! version_number_key = "Version"
! version_string_key = "Full Description"
app_name = "Outlook"
! cur_ver_string = get_version_string(app_name, version_string_key)
! cur_ver_num = get_version_number(app_name, version_number_key)
try:
--- 593,602 ----
def CheckLatestVersion(manager):
! from spambayes.Version import get_current_version, get_version, \
! get_download_page, fetch_latest_dict
app_name = "Outlook"
! ver_current = get_current_version()
! cur_ver_string = ver_current.get_long_version(ADDIN_DISPLAY_NAME)
try:
***************
*** 608,615 ****
latest = fetch_latest_dict()
SetWaitCursor(0)
! latest_ver_string = get_version_string(app_name, version_string_key,
! version_dict=latest)
! latest_ver_num = get_version_number(app_name, version_number_key,
! version_dict=latest)
except:
print "Error checking the latest version"
--- 604,609 ----
latest = fetch_latest_dict()
SetWaitCursor(0)
! ver_latest = get_version(app_name, version_dict=latest)
! latest_ver_string = ver_latest.get_long_version(ADDIN_DISPLAY_NAME)
except:
print "Error checking the latest version"
***************
*** 622,628 ****
return
! print "Current version is %s, latest is %s." % (cur_ver_num, latest_ver_num)
! if latest_ver_num > cur_ver_num:
! url = get_version_string(app_name, "Download Page", version_dict=latest)
msg = _("You are running %s\r\n\r\nThe latest available version is %s" \
"\r\n\r\nThe download page for the latest version is\r\n%s" \
--- 616,622 ----
return
! print "Current version is %s, latest is %s." % (str(ver_current), str(ver_latest))
! if ver_latest > ver_current:
! url = get_download_page(app_name, version_dict=latest)
msg = _("You are running %s\r\n\r\nThe latest available version is %s" \
"\r\n\r\nThe download page for the latest version is\r\n%s" \
***************
*** 1282,1291 ****
# Only now will the import of "spambayes.Version" work, as the
# manager is what munges sys.path for us.
! from spambayes.Version import get_version_string
! version_key = "Full Description"
! if hasattr(sys, "frozen"): version_key += " Binary"
! print "%s starting (with engine %s)" % \
! (get_version_string("Outlook", version_key),
! get_version_string())
major, minor, spack, platform, ver_str = win32api.GetVersionEx()
print "on Windows %d.%d.%d (%s)" % \
--- 1276,1284 ----
# Only now will the import of "spambayes.Version" work, as the
# manager is what munges sys.path for us.
! from spambayes.Version import get_current_version
! v = get_current_version()
! vstring = v.get_long_version(ADDIN_DISPLAY_NAME)
! if not hasattr(sys, "frozen"): vstring += " from source"
! print vstring
major, minor, spack, platform, ver_str = win32api.GetVersionEx()
print "on Windows %d.%d.%d (%s)" % \
From anadelonbrin at users.sourceforge.net Mon Dec 27 03:26:36 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Mon Dec 27 03:26:40 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.148,1.149
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3753/Outlook2000
Modified Files:
addin.py
Log Message:
Fix typo.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.148
retrieving revision 1.149
diff -C2 -d -r1.148 -r1.149
*** addin.py 23 Dec 2004 18:14:16 -0000 1.148
--- addin.py 27 Dec 2004 02:26:28 -0000 1.149
***************
*** 692,696 ****
self.manager.score(msgstore_message))
msgstore_message.t = True
! self.manager.classifier_data.message_db.store_msg(msg)
self.manager.classifier_data.dirty = True
# Record the original folder, in case this message is not where
--- 692,696 ----
self.manager.score(msgstore_message))
msgstore_message.t = True
! self.manager.classifier_data.message_db.store_msg(msgstore_message)
self.manager.classifier_data.dirty = True
# Record the original folder, in case this message is not where
From anadelonbrin at users.sourceforge.net Wed Dec 29 07:19:41 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 29 07:19:45 2004
Subject: [Spambayes-checkins] spambayes/spambayes Stats.py,1.13,1.14
Message-ID:
Update of /cvsroot/spambayes/spambayes/spambayes
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1717/spambayes
Modified Files:
Stats.py
Log Message:
Skip messages that don't have a modified date - for Outlook, this will make no difference
for anyone except people using code from CVS for the last few weeks. For others,
this means that the stats will be refreshed when starting to use 1.1, but that's
ok, really.
Clean up another function.
Index: Stats.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/spambayes/Stats.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -d -r1.13 -r1.14
*** Stats.py 22 Dec 2004 00:25:45 -0000 1.13
--- Stats.py 29 Dec 2004 06:19:38 -0000 1.14
***************
*** 117,123 ****
self.messageinfo_db.load_msg(m)
# Skip ones that are too old.
! if self.from_date and m.date_modified and \
! m.date_modified < self.from_date:
continue
--- 117,126 ----
self.messageinfo_db.load_msg(m)
+ # Skip all old messages that don't have a date.
+ if m.date_modified is None:
+ continue
+
# Skip ones that are too old.
! if self.from_date and m.date_modified < self.from_date:
continue
***************
*** 150,167 ****
def _CombineSessionAndTotal(self):
totals = self.totals
! num_seen = self.num_ham + self.num_spam + self.num_unsure + \
! totals["num_ham"] + totals["num_spam"] + \
! totals["num_unsure"]
! num_ham = self.num_ham + totals["num_ham"]
! num_spam = self.num_spam + totals["num_spam"]
! num_unsure = self.num_unsure + totals["num_unsure"]
! num_trained_ham = self.num_trained_ham + totals["num_trained_ham"]
! num_trained_ham_fp = self.num_trained_ham_fp + \
! totals["num_trained_ham_fp"]
! num_trained_spam = self.num_trained_spam + \
! totals["num_trained_spam"]
! num_trained_spam_fn = self.num_trained_spam_fn + \
! totals["num_trained_spam_fn"]
! return locals()
def _CalculateAdditional(self, data):
--- 153,171 ----
def _CombineSessionAndTotal(self):
totals = self.totals
! data = {}
! data["num_ham"] = self.num_ham + totals["num_ham"]
! data["num_spam"] = self.num_spam + totals["num_spam"]
! data["num_unsure"] = self.num_unsure + totals["num_unsure"]
! data["num_seen"] = data["num_ham"] + data["num_spam"] + \
! data["num_unsure"]
! data["num_trained_ham"] = self.num_trained_ham + \
! totals["num_trained_ham"]
! data["num_trained_ham_fp"] = self.num_trained_ham_fp + \
! totals["num_trained_ham_fp"]
! data["num_trained_spam"] = self.num_trained_spam + \
! totals["num_trained_spam"]
! data["num_trained_spam_fn"] = self.num_trained_spam_fn + \
! totals["num_trained_spam_fn"]
! return data
def _CalculateAdditional(self, data):
From anadelonbrin at users.sourceforge.net Wed Dec 29 07:22:34 2004
From: anadelonbrin at users.sourceforge.net (Tony Meyer)
Date: Wed Dec 29 07:22:38 2004
Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py, 1.149,
1.150 filter.py, 1.46, 1.47 msgstore.py, 1.98, 1.99
Message-ID:
Update of /cvsroot/spambayes/spambayes/Outlook2000
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2198/Outlook2000
Modified Files:
addin.py filter.py msgstore.py
Log Message:
Must save message after remembering the folder.
Report the last modified date in the "show clues" message. I'm not sure this is the
best way to put it, but I'll think more about it and change it if I come up with
something better. Feel free to think of something yourself!
Save classifier_data if it is dirty.
Add modified_date to the persistent attributes now that it's not automatically stored.
Index: addin.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v
retrieving revision 1.149
retrieving revision 1.150
diff -C2 -d -r1.149 -r1.150
*** addin.py 27 Dec 2004 02:26:28 -0000 1.149
--- addin.py 29 Dec 2004 06:22:24 -0000 1.150
***************
*** 469,472 ****
--- 469,484 ----
push("# ham trained on: %d
\n" % c.nham)
push("# spam trained on: %d
\n" % c.nspam)
+ push("
\n")
+
+ # Report last modified date.
+ modified_date = msgstore_message.date_modified
+ if modified_date:
+ from time import localtime, strftime
+ modified_date = localtime(modified_date)
+ date_string = strftime("%a, %d %b %Y %I:%M:%S %p", modified_date)
+ push("As at %s:
\n" % (date_string,))
+ else:
+ push("The last time this message was classified or trained:
\n")
+
# Score when the message was classified - this will hopefully help
# people realise that it may not necessarily be the same, and will
***************
*** 482,497 ****
else:
original_class = "good"
- push("
\n")
if original_score is None:
! push("This message has not been filtered.")
else:
original_score = round(original_score)
! push("When this message was last filtered, it was classified " \
! "as %s (it scored %d%%)." % (original_class, original_score))
# Report whether this message has been trained or not.
push("
\n")
! push("This message has %sbeen trained%s." % \
{False : ("", " as ham"), True : ("", " as spam"),
None : ("not ", "")}[msgstore_message.t])
# Format the clues.
push("%s Significant Tokens
\n" % len(clues))
--- 494,509 ----
else:
original_class = "good"
if original_score is None:
! push("This message had not been filtered.")
else:
original_score = round(original_score)
! push("This message was classified as %s (it scored %d%%)." % \
! (original_class, original_score))
# Report whether this message has been trained or not.
push("
\n")
! push("This message had %sbeen trained%s." % \
{False : ("", " as ham"), True : ("", " as spam"),
None : ("not ", "")}[msgstore_message.t])
+
# Format the clues.
push("%s Significant Tokens
\n" % len(clues))
***************
*** 697,700 ****
--- 709,713 ----
# it was after filtering, or has never been filtered.
msgstore_message.RememberMessageCurrentFolder()
+ msgstore_message.Save()
# Must train before moving, else we lose the message!
subject = msgstore_message.GetSubject()
Index: filter.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
retrieving revision 1.46
retrieving revision 1.47
diff -C2 -d -r1.46 -r1.47
*** filter.py 23 Dec 2004 02:05:14 -0000 1.46
--- filter.py 29 Dec 2004 06:22:31 -0000 1.47
***************
*** 113,116 ****
--- 113,117 ----
mgr.classifier_data.message_db.store_msg(msg)
mgr.classifier_data.dirty = True
+ mgr.classifier_data.SavePostIncrementalTrain()
return disposition
except:
Index: msgstore.py
===================================================================
RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v
retrieving revision 1.98
retrieving revision 1.99
diff -C2 -d -r1.98 -r1.99
*** msgstore.py 22 Dec 2004 01:22:00 -0000 1.98
--- msgstore.py 29 Dec 2004 06:22:31 -0000 1.99
***************
*** 809,813 ****
# For use with the spambayes.message messageinfo database.
! self.stored_attributes = ['c', 't', 'original_folder']
self.t = None
self.c = None
--- 809,814 ----
# For use with the spambayes.message messageinfo database.
! self.stored_attributes = ['c', 't', 'original_folder',
! 'date_modified']
self.t = None
self.c = None