Skip to content

Commit 006116c

Browse files
author
Richard Jones
committed
A few big changes in this commit:
1. The current indexer has been moved to backends/indexer_dbm in anticipation of my writing an indexer_rdbms, 2. Changed indexer invocation during create / set to follow the pattern set by the metakit backend, which was much cleaner, and 3. The "content" property of FileClass is now mutable in all but the metakit backend. Metakit needs to be changed to support the editing of "content". Hey, and I learnt today that the metakit backend implements its own indexer. How about that... :)
1 parent 2a9a41d commit 006116c

File tree

14 files changed

+235
-147
lines changed

14 files changed

+235
-147
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Fixed:
5555
- the mail gateway now searches recursively for the text/plain and the
5656
attachments of a message (sf bug 841241).
5757
- fixed display of feedback messages in some situations (sf bug 739545)
58+
- fixed ability to edit "content" property (sf bug 914062)
5859

5960
Cleanup:
6061
- replace curuserid attribute on Database with the extended getuid() method.

roundup/backends/back_anydbm.py

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: back_anydbm.py,v 1.138 2004-03-18 01:58:45 richard Exp $
18+
#$Id: back_anydbm.py,v 1.139 2004-03-19 04:47:59 richard Exp $
1919
'''This module defines a backend that saves the hyperdatabase in a
2020
database chosen by anydbm. It is guaranteed to always be available in python
2121
versions >2.1.1 (the dumbdbm fallback in 2.1.1 and earlier has several
@@ -37,7 +37,7 @@
3737
from roundup import hyperdb, date, password, roundupdb, security
3838
from blobfiles import FileStorage
3939
from sessions_dbm import Sessions, OneTimeKeys
40-
from roundup.indexer import Indexer
40+
from indexer_dbm import Indexer
4141
from roundup.backends import locking
4242
from roundup.hyperdb import String, Password, Date, Interval, Link, \
4343
Multilink, DatabaseError, Boolean, Number, Node
@@ -882,6 +882,7 @@ def create_inner(self, **propvalues):
882882
elif isinstance(prop, String):
883883
if type(value) != type('') and type(value) != type(u''):
884884
raise TypeError, 'new property "%s" not a string'%key
885+
self.db.indexer.add_text((self.classname, newid, key), value)
885886

886887
elif isinstance(prop, Password):
887888
if not isinstance(value, password.Password):
@@ -1143,6 +1144,15 @@ class or a KeyError is raised.
11431144
These operations trigger detectors and can be vetoed. Attempts
11441145
to modify the "creation" or "activity" properties cause a KeyError.
11451146
'''
1147+
self.fireAuditors('set', nodeid, propvalues)
1148+
oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid))
1149+
propvalues = self.set_inner(nodeid, **propvalues)
1150+
self.fireReactors('set', nodeid, oldvalues)
1151+
return propvalues
1152+
1153+
def set_inner(self, nodeid, **propvalues):
1154+
''' Called by set, in-between the audit and react calls.
1155+
'''
11461156
if not propvalues:
11471157
return propvalues
11481158

@@ -1155,11 +1165,6 @@ class or a KeyError is raised.
11551165
if self.db.journaltag is None:
11561166
raise DatabaseError, 'Database open read-only'
11571167

1158-
self.fireAuditors('set', nodeid, propvalues)
1159-
# Take a copy of the node dict so that the subsequent set
1160-
# operation doesn't modify the oldvalues structure.
1161-
oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid))
1162-
11631168
node = self.db.getnode(self.classname, nodeid)
11641169
if node.has_key(self.db.RETIRED_FLAG):
11651170
raise IndexError
@@ -1290,6 +1295,8 @@ class or a KeyError is raised.
12901295
elif isinstance(prop, String):
12911296
if value is not None and type(value) != type('') and type(value) != type(u''):
12921297
raise TypeError, 'new property "%s" not a string'%propname
1298+
self.db.indexer.add_text((self.classname, nodeid, propname),
1299+
value)
12931300

12941301
elif isinstance(prop, Password):
12951302
if not isinstance(value, password.Password):
@@ -1331,9 +1338,7 @@ class or a KeyError is raised.
13311338
if self.do_journal:
13321339
self.db.addjournal(self.classname, nodeid, 'set', journalvalues)
13331340

1334-
self.fireReactors('set', nodeid, oldvalues)
1335-
1336-
return propvalues
1341+
return propvalues
13371342

13381343
def retire(self, nodeid):
13391344
'''Retire a node.
@@ -1946,20 +1951,18 @@ def addprop(self, **properties):
19461951
self.properties.update(properties)
19471952

19481953
def index(self, nodeid):
1949-
'''Add (or refresh) the node to search indexes
1950-
'''
1954+
''' Add (or refresh) the node to search indexes '''
19511955
# find all the String properties that have indexme
19521956
for prop, propclass in self.getprops().items():
1953-
if isinstance(propclass, String) and propclass.indexme:
1957+
if isinstance(propclass, hyperdb.String) and propclass.indexme:
1958+
# index them under (classname, nodeid, property)
19541959
try:
19551960
value = str(self.get(nodeid, prop))
19561961
except IndexError:
1957-
# node no longer exists - entry should be removed
1958-
self.db.indexer.purge_entry((self.classname, nodeid, prop))
1959-
else:
1960-
# and index them under (classname, nodeid, property)
1961-
self.db.indexer.add_text((self.classname, nodeid, prop),
1962-
value)
1962+
# node has been destroyed
1963+
continue
1964+
self.db.indexer.add_text((self.classname, nodeid, prop), value)
1965+
19631966

19641967
#
19651968
# Detector interface
@@ -2012,8 +2015,15 @@ def create(self, **propvalues):
20122015
content = propvalues['content']
20132016
del propvalues['content']
20142017

2018+
# make sure we have a MIME type
2019+
mime_type = propvalues.get('type', self.default_mime_type)
2020+
20152021
# do the database create
2016-
newid = Class.create_inner(self, **propvalues)
2022+
newid = self.create_inner(**propvalues)
2023+
2024+
# and index!
2025+
self.db.indexer.add_text((self.classname, newid, 'content'), content,
2026+
mime_type)
20172027

20182028
# fire reactors
20192029
self.fireReactors('create', newid, None)
@@ -2059,6 +2069,35 @@ def get(self, nodeid, propname, default=_marker, cache=1):
20592069
else:
20602070
return Class.get(self, nodeid, propname)
20612071

2072+
def set(self, itemid, **propvalues):
2073+
''' Snarf the "content" propvalue and update it in a file
2074+
'''
2075+
self.fireAuditors('set', itemid, propvalues)
2076+
oldvalues = copy.deepcopy(self.db.getnode(self.classname, itemid))
2077+
2078+
# now remove the content property so it's not stored in the db
2079+
content = None
2080+
if propvalues.has_key('content'):
2081+
content = propvalues['content']
2082+
del propvalues['content']
2083+
2084+
# do the database create
2085+
propvalues = self.set_inner(itemid, **propvalues)
2086+
2087+
# do content?
2088+
if content:
2089+
# store and index
2090+
self.db.storefile(self.classname, itemid, None, content)
2091+
mime_type = propvalues.get('type', self.get(itemid, 'type'))
2092+
if not mime_type:
2093+
mime_type = self.default_mime_type
2094+
self.db.indexer.add_text((self.classname, itemid, 'content'),
2095+
content, mime_type)
2096+
2097+
# fire reactors
2098+
self.fireReactors('set', itemid, oldvalues)
2099+
return propvalues
2100+
20622101
def getprops(self, protected=1):
20632102
''' In addition to the actual properties on the node, these methods
20642103
provide the "content" property. If the "protected" flag is true,
@@ -2069,27 +2108,6 @@ def getprops(self, protected=1):
20692108
d['content'] = hyperdb.String()
20702109
return d
20712110

2072-
def index(self, nodeid):
2073-
''' Index the node in the search index.
2074-
2075-
We want to index the content in addition to the normal String
2076-
property indexing.
2077-
'''
2078-
# perform normal indexing
2079-
Class.index(self, nodeid)
2080-
2081-
# get the content to index
2082-
content = self.get(nodeid, 'content')
2083-
2084-
# figure the mime type
2085-
if self.properties.has_key('type'):
2086-
mime_type = self.get(nodeid, 'type')
2087-
else:
2088-
mime_type = self.default_mime_type
2089-
2090-
# and index!
2091-
self.db.indexer.add_text((self.classname, nodeid, 'content'), content,
2092-
mime_type)
20932111

20942112
# deviation from spec - was called ItemClass
20952113
class IssueClass(Class, roundupdb.IssueClass):

roundup/backends/back_metakit.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_metakit.py,v 1.62 2004-03-18 01:58:45 richard Exp $
1+
# $Id: back_metakit.py,v 1.63 2004-03-19 04:47:59 richard Exp $
22
'''Metakit backend for Roundup, originally by Gordon McMillan.
33
44
Known Current Bugs:
@@ -45,7 +45,7 @@
4545
import metakit
4646
from sessions_dbm import Sessions, OneTimeKeys
4747
import re, marshal, os, sys, time, calendar
48-
from roundup import indexer
48+
from indexer_dbm import Indexer
4949
import locking
5050
from roundup.date import Range
5151

@@ -1783,7 +1783,7 @@ def __init__(self, db, classname, **properties):
17831783

17841784
CURVERSION = 2
17851785

1786-
class Indexer(indexer.Indexer):
1786+
class Indexer(Indexer):
17871787
disallows = {'THE':1, 'THIS':1, 'ZZZ':1, 'THAT':1, 'WITH':1}
17881788
def __init__(self, path, datadb):
17891789
self.path = os.path.join(path, 'index.mk4')

roundup/backends/back_mysql.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ def drop_multilink_table_indexes(self, classname, ml):
244244
'%s_%s_l_idx'%(classname, ml),
245245
'%s_%s_n_idx'%(classname, ml)
246246
]
247+
table_name = '%s_%s'%(classname, ml)
247248
for index_name in l:
248249
if not self.sql_index_exists(table_name, index_name):
249250
continue

roundup/backends/back_postgresql.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,30 +146,26 @@ def create_class_table(self, spec):
146146
cols, mls = self.determine_columns(spec.properties.items())
147147
cols.append('id')
148148
cols.append('__retired__')
149-
scols = ',' . join(['"%s" VARCHAR(255)' % x for x in cols])
149+
scols = ',' . join(['"%s" VARCHAR(255)'%x for x in cols])
150150
sql = 'CREATE TABLE "_%s" (%s)' % (spec.classname, scols)
151-
152151
if __debug__:
153-
print >>hyperdb.DEBUG, 'create_class', (self, sql)
154-
152+
print >>hyperdb.DEBUG, 'create_class_table', (self, sql)
155153
self.cursor.execute(sql)
156154
self.create_class_table_indexes(spec)
157155
return cols, mls
158156

159157
def create_journal_table(self, spec):
160-
cols = ',' . join(['"%s" VARCHAR(255)' % x
161-
for x in 'nodeid date tag action params' . split()])
158+
cols = ',' . join(['"%s" VARCHAR(255)'%x
159+
for x in 'nodeid date tag action params' . split()])
162160
sql = 'CREATE TABLE "%s__journal" (%s)'%(spec.classname, cols)
163-
164161
if __debug__:
165-
print >>hyperdb.DEBUG, 'create_class', (self, sql)
166-
162+
print >>hyperdb.DEBUG, 'create_journal_table', (self, sql)
167163
self.cursor.execute(sql)
168164
self.create_journal_table_indexes(spec)
169165

170166
def create_multilink_table(self, spec, ml):
171167
sql = '''CREATE TABLE "%s_%s" (linkid VARCHAR(255),
172-
nodeid VARCHAR(255))''' % (spec.classname, ml)
168+
nodeid VARCHAR(255))'''%(spec.classname, ml)
173169

174170
if __debug__:
175171
print >>hyperdb.DEBUG, 'create_class', (self, sql)

roundup/backends/blobfiles.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: blobfiles.py,v 1.11 2004-02-11 23:55:09 richard Exp $
18+
#$Id: blobfiles.py,v 1.12 2004-03-19 04:47:59 richard Exp $
1919
'''This module exports file storage for roundup backends.
2020
Files are stored into a directory hierarchy.
2121
'''
@@ -77,12 +77,14 @@ def storefile(self, classname, nodeid, property, content):
7777
if not os.path.exists(os.path.dirname(name)):
7878
os.makedirs(os.path.dirname(name))
7979

80-
# open the temp file for writing
81-
open(name + '.tmp', 'wb').write(content)
82-
83-
# save off the commit action
84-
self.transactions.append((self.doStoreFile, (classname, nodeid,
85-
property)))
80+
# save to a temp file
81+
name = name + '.tmp'
82+
# make sure we don't register the rename action more than once
83+
if not os.path.exists(name):
84+
# save off the rename action
85+
self.transactions.append((self.doStoreFile, (classname, nodeid,
86+
property)))
87+
open(name, 'wb').write(content)
8688

8789
def getfile(self, classname, nodeid, property):
8890
'''Get the content of the file in the database.
@@ -115,6 +117,11 @@ def doStoreFile(self, classname, nodeid, property, **databases):
115117
# determine the name of the file to write to
116118
name = self.filename(classname, nodeid, property)
117119

120+
# content is being updated (and some platforms, eg. win32, won't
121+
# let us rename over the top of the old file)
122+
if os.path.exists(name):
123+
os.remove(name)
124+
118125
# the file is currently ".tmp" - move it to its real name to commit
119126
os.rename(name+".tmp", name)
120127

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
# that promote freedom, but obviously am giving up any rights
1515
# to compel such.
1616
#
17-
#$Id: indexer.py,v 1.18 2004-02-11 23:55:08 richard Exp $
17+
#$Id: indexer_dbm.py,v 1.1 2004-03-19 04:47:59 richard Exp $
1818
'''This module provides an indexer class, RoundupIndexer, that stores text
1919
indices in a roundup instance. This class makes searching the content of
2020
messages, string properties and text files possible.
2121
'''
2222
__docformat__ = 'restructuredtext'
2323

2424
import os, shutil, re, mimetypes, marshal, zlib, errno
25-
from hyperdb import Link, Multilink
25+
from roundup.hyperdb import Link, Multilink
2626

2727
class Indexer:
2828
'''Indexes information from roundup's hyperdb to allow efficient
@@ -129,7 +129,7 @@ def text_splitter(self, text):
129129
"""Split text/plain string into a list of words
130130
"""
131131
# case insensitive
132-
text = text.upper()
132+
text = str(text).upper()
133133

134134
# Split the raw text, losing anything longer than 25 characters
135135
# since that'll be gibberish (encoded text or somesuch) or shorter
@@ -341,4 +341,9 @@ def index_loaded(self):
341341
return (hasattr(self,'fileids') and hasattr(self,'files') and
342342
hasattr(self,'words'))
343343

344+
345+
def rollback(self):
346+
''' load last saved index info. '''
347+
self.load_index(reload=1)
348+
344349
# vim: set filetype=python ts=4 sw=4 et si

0 commit comments

Comments
 (0)