summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 22a656f)
raw | patch | inline | side by side (parent: 22a656f)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Thu, 26 Sep 2002 03:04:24 +0000 (03:04 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Thu, 26 Sep 2002 03:04:24 +0000 (03:04 +0000) |
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1248 57a73879-2fb5-44c3-a270-3262357dd7e2
diff --git a/CHANGES.txt b/CHANGES.txt
index 003f1b82bed2452bb164837d0001752dc50ac992..ecaf45f36f0af5434d9576149cd42910e74aab58 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
- handle stupid mailers that QUOTE their Re; 'Re: "[issue1] bla blah"'
- giving a user a Role that doesn't exist doesn't break stuff any more
- revamped user guide, customisation guide, added maintenance guide
+- merge Zope Collector #580 fix from ZPT CVS trunk
2002-09-13 0.5.0 beta2
index 13487985b1edc937a7652ea9a97b7d0460b165fa..ea2533a31b86ad70185de6f407f2f9e0952664ac 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: back_anydbm.py,v 1.85 2002-09-24 01:59:28 richard Exp $
+#$Id: back_anydbm.py,v 1.86 2002-09-26 03:04:24 richard Exp $
'''
This module defines a backend that saves the hyperdatabase in a database
chosen by anydbm. It is guaranteed to always be available in python
if __debug__:
print >>hyperdb.DEBUG, 'packjournal', (self, pack_before)
+ pack_before = pack_before.serialise()
for classname in self.getclasses():
# get the journal db
db_name = 'journals.%s'%classname
# unpack the entry
(nodeid, date_stamp, self.journaltag, action,
params) = entry
- date_stamp = date.Date(date_stamp)
# if the entry is after the pack date, _or_ the initial
# create entry, then it stays
if date_stamp > pack_before or action == 'create':
l.append(entry)
- elif action == 'set':
- # grab the last set entry to keep information on
- # activity
- last_set_entry = entry
- if last_set_entry:
- date_stamp = last_set_entry[1]
- # if the last set entry was made after the pack date
- # then it is already in the list
- if date_stamp < pack_before:
- l.append(last_set_entry)
db[key] = marshal.dumps(l)
if db_type == 'gdbm':
db.reorganize()
def find(self, **propspec):
'''Get the ids of nodes in this class which link to the given nodes.
- 'propspec' consists of keyword args propname={nodeid:1,}
- 'propname' must be the name of a property in this class, or a
- KeyError is raised. That property must be a Link or Multilink
- property, or a TypeError is raised.
+ 'propspec' consists of keyword args propname=nodeid or
+ propname={nodeid:1, }
+ 'propname' must be the name of a property in this class, or a
+ KeyError is raised. That property must be a Link or
+ Multilink property, or a TypeError is raised.
Any node in this class whose 'propname' property links to any of the
nodeids will be returned. Used by the full text indexing, which knows
- that "foo" occurs in msg1, msg3 and file7, so we have hits on these issues:
+ that "foo" occurs in msg1, msg3 and file7, so we have hits on these
+ issues:
+
db.issue.find(messages={'1':1,'3':1}, files={'7':1})
'''
propspec = propspec.items()
index e14a12c6f7f5a9ba4f9367fd8138996461543411..1b9b05b38d7a29daf09d53cb71f7bc7f50aa024c 100644 (file)
-# $Id: back_gadfly.py,v 1.26 2002-09-24 01:59:28 richard Exp $
+# $Id: back_gadfly.py,v 1.27 2002-09-26 03:04:24 richard Exp $
__doc__ = '''
About Gadfly
============
'''
-from roundup.backends.rdbms_common import *
+# standard python modules
+import sys, os, time, re, errno, weakref, copy
+
+# roundup modules
+from roundup import hyperdb, date, password, roundupdb, security
+from roundup.hyperdb import String, Password, Date, Interval, Link, \
+ Multilink, DatabaseError, Boolean, Number
+
+# basic RDBMS backen implementation
+from roundup.backends import rdbms_common
# the all-important gadfly :)
import gadfly
import gadfly.client
import gadfly.database
-class Database(Database):
+class Database(rdbms_common.Database):
# char to use for positional arguments
arg = '?'
# return the IDs
return [row[0] for row in l]
-class Class(GadflyClass, Class):
+ def find(self, **propspec):
+ ''' Overload to filter out duplicates in the result
+ '''
+ d = {}
+ for k in rdbms_common.Class.find(self, **propspec):
+ d[k] = 1
+ return d.keys()
+
+class Class(GadflyClass, rdbms_common.Class):
pass
-class IssueClass(GadflyClass, IssueClass):
+class IssueClass(GadflyClass, rdbms_common.IssueClass):
pass
-class FileClass(GadflyClass, FileClass):
+class FileClass(GadflyClass, rdbms_common.FileClass):
pass
index f0acaee844380fd52efd0e0b019a00adec0af9d2..7c48bf563c0a057e2036a64a8ea309f205d91200 100644 (file)
-# $Id: rdbms_common.py,v 1.18 2002-09-25 05:27:29 richard Exp $
+# $Id: rdbms_common.py,v 1.19 2002-09-26 03:04:24 richard Exp $
# standard python modules
import sys, os, time, re, errno, weakref, copy
if self.db.journaltag is None:
raise DatabaseError, 'Database open read-only'
- sql = 'update _%s set __retired__=1 where id=%s'%(self.classname,
- self.db.arg)
+ # use the arg for __retired__ to cope with any odd database type
+ # conversion (hello, sqlite)
+ sql = 'update _%s set __retired__=%s where id=%s'%(self.classname,
+ self.db.arg, self.db.arg)
if __debug__:
print >>hyperdb.DEBUG, 'retire', (self, sql, nodeid)
- self.db.cursor.execute(sql, (nodeid,))
+ self.db.cursor.execute(sql, (1, nodeid))
def is_retired(self, nodeid):
'''Return true if the node is rerired
if not self.key:
raise TypeError, 'No key property set for class %s'%self.classname
- sql = '''select id from _%s where _%s=%s
- and __retired__ != '1' '''%(self.classname, self.key,
- self.db.arg)
- self.db.sql(sql, (keyvalue,))
+ # use the arg to handle any odd database type conversion (hello,
+ # sqlite)
+ sql = "select id from _%s where _%s=%s and __retired__ <> %s"%(
+ self.classname, self.key, self.db.arg, self.db.arg)
+ self.db.sql(sql, (keyvalue, 1))
# see if there was a result that's not retired
row = self.db.sql_fetchone()
def find(self, **propspec):
'''Get the ids of nodes in this class which link to the given nodes.
- 'propspec' consists of keyword args propname={nodeid:1,}
+ 'propspec' consists of keyword args propname=nodeid or
+ propname={nodeid:1, }
'propname' must be the name of a property in this class, or a
KeyError is raised. That property must be a Link or Multilink
property, or a TypeError is raised.
'''
if __debug__:
print >>hyperdb.DEBUG, 'find', (self, propspec)
+
+ # shortcut
if not propspec:
return []
- queries = []
- tables = []
+
+ # validate the args
+ props = self.getprops()
+ propspec = propspec.items()
+ for propname, nodeids in propspec:
+ # check the prop is OK
+ prop = props[propname]
+ if not isinstance(prop, Link) and not isinstance(prop, Multilink):
+ raise TypeError, "'%s' not a Link/Multilink property"%propname
+
+ # first, links
+ where = []
allvalues = ()
- for prop, values in propspec.items():
- allvalues += tuple(values.keys())
- a = self.db.arg
+ a = self.db.arg
+ for prop, values in propspec:
+ if not isinstance(props[prop], hyperdb.Link):
+ continue
+ if type(values) is type(''):
+ allvalues += (values,)
+ where.append('_%s = %s'%(prop, a))
+ else:
+ allvalues += tuple(values.keys())
+ where.append('_%s in (%s)'%(prop, ','.join([a]*len(values))))
+ tables = []
+ if where:
+ tables.append('select id as nodeid from _%s where %s'%(
+ self.classname, ' and '.join(where)))
+
+ # now multilinks
+ for prop, values in propspec:
+ if not isinstance(props[prop], hyperdb.Multilink):
+ continue
+ if type(values) is type(''):
+ allvalues += (values,)
+ s = a
+ else:
+ allvalues += tuple(values.keys())
+ s = ','.join([a]*len(values))
tables.append('select nodeid from %s_%s where linkid in (%s)'%(
- self.classname, prop, ','.join([a for x in values.keys()])))
- sql = '\nintersect\n'.join(tables)
+ self.classname, prop, s))
+ sql = '\nunion\n'.join(tables)
self.db.sql(sql, allvalues)
l = [x[0] for x in self.db.sql_fetchall()]
if __debug__:
diff --git a/test/test_db.py b/test/test_db.py
index 315ab2fd3e71bd55bc39fe8c24b2c59513f3c6e0..bd1b20b9b5783527ca18de205d32f14b3c91a5af 100644 (file)
--- a/test/test_db.py
+++ b/test/test_db.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: test_db.py,v 1.55 2002-09-24 01:59:44 richard Exp $
+# $Id: test_db.py,v 1.56 2002-09-26 03:04:24 richard Exp $
import unittest, os, shutil, time
self.db.commit()
# sleep for at least a second, then get a date to pack at
- time.sleep(2)
+ time.sleep(1)
pack_before = date.Date('.')
- time.sleep(2)
- # one more entry
+ # wait another second and add one more entry
+ time.sleep(1)
self.db.issue.set(id, status='3')
self.db.commit()
+ jlen = len(self.db.getjournal('issue', id))
# pack
self.db.pack(pack_before)
- journal = self.db.getjournal('issue', id)
# we should have the create and last set entries now
- self.assertEqual(2, len(journal))
+ self.assertEqual(jlen-1, len(self.db.getjournal('issue', id)))
def testIDGeneration(self):
id1 = self.db.issue.create(title="spam", status='1')
self.assertEquals(self.db.indexer.search(['flebble'], self.db.issue),
{'1': {}})
+ #
+ # searching tests follow
+ #
+ def testFind(self):
+ self.db.user.create(username='test')
+ ids = []
+ ids.append(self.db.issue.create(status="1", nosy=['1']))
+ oddid = self.db.issue.create(status="2", nosy=['2'])
+ ids.append(self.db.issue.create(status="1", nosy=['1','2']))
+ self.db.issue.create(status="3", nosy=['1'])
+ ids.sort()
+
+ # should match first and third
+ got = self.db.issue.find(status='1')
+ got.sort()
+ self.assertEqual(got, ids)
+
+ # none
+ self.assertEqual(self.db.issue.find(status='4'), [])
+
+ # should match first three
+ got = self.db.issue.find(status='1', nosy='2')
+ got.sort()
+ ids.append(oddid)
+ ids.sort()
+ self.assertEqual(got, ids)
+
+ # none
+ self.assertEqual(self.db.issue.find(status='4', nosy='3'), [])
+
def testStringFind(self):
ids = []
ids.append(self.db.issue.create(title="spam"))
id2 = self.db.issue.create(title="eggs", status='2')
self.assertNotEqual(id1, id2)
+ def testFilteringString(self):
+ ae, filt = self.filteringSetup()
+ ae(filt(None, {'title': 'issue one'}, ('+','id'), (None,None)), ['1'])
+ # XXX gadfly can't do substring LIKE searches
+ #ae(filt(None, {'title': 'issue'}, ('+','id'), (None,None)),
+ # ['1','2','3'])
+
class gadflyReadOnlyDBTestCase(anydbmReadOnlyDBTestCase):
def setUp(self):
from roundup.backends import gadfly
diff --git a/test/test_mailgw.py b/test/test_mailgw.py
index 71293a5fdfbe00c133d349986d14d7ef071cd899..af81ce6a81a627855944b9c63f67d6475046133e 100644 (file)
--- a/test/test_mailgw.py
+++ b/test/test_mailgw.py
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
-# $Id: test_mailgw.py,v 1.31 2002-09-20 05:08:00 richard Exp $
+# $Id: test_mailgw.py,v 1.32 2002-09-26 03:04:24 richard Exp $
import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys, difflib
A message with first part encoded (encoded oe =F6)
+----------
+status: unread -> chatting
+_________________________________________________________________________
+"Roundup issue tracker" <issue_tracker@your.tracker.email.domain.example>
+http://your.tracker.url.example/issue1
+_________________________________________________________________________
+''')
+
+ def testFollowupStupidQuoting(self):
+ self.doNewIssue()
+
+ message = cStringIO.StringIO('''Content-Type: text/plain;
+ charset="iso-8859-1"
+From: richard <richard@test>
+To: issue_tracker@your.tracker.email.domain.example
+Message-Id: <followup_dummy_id>
+In-Reply-To: <dummy_test_message_id>
+Subject: Re: "[issue1] Testing... "
+
+This is a followup
+''')
+ handler = self.instance.MailGW(self.instance, self.db)
+ handler.trapExceptions = 0
+ handler.main(message)
+
+ self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
+'''FROM: roundup-admin@your.tracker.email.domain.example
+TO: chef@bork.bork.bork
+Content-Type: text/plain
+Subject: [issue1] Testing...
+To: chef@bork.bork.bork
+From: "richard" <issue_tracker@your.tracker.email.domain.example>
+Reply-To: "Roundup issue tracker" <issue_tracker@your.tracker.email.domain.example>
+MIME-Version: 1.0
+Message-Id: <followup_dummy_id>
+In-Reply-To: <dummy_test_message_id>
+X-Roundup-Name: Roundup issue tracker
+Content-Transfer-Encoding: quoted-printable
+
+
+richard <richard@test> added the comment:
+
+This is a followup
+
+
----------
status: unread -> chatting
_________________________________________________________________________