Update News to use commands.wrap and switch from an SQLite db to a flatfile

mapping.
This commit is contained in:
James Vega 2004-10-06 21:56:06 +00:00
parent edc698e48b
commit 0242a8619d
2 changed files with 162 additions and 216 deletions

View File

@ -34,8 +34,6 @@ dates.
__revision__ = "$Id$" __revision__ = "$Id$"
import supybot.plugins as plugins
import os import os
import time import time
@ -43,19 +41,24 @@ import supybot.dbi as dbi
import supybot.conf as conf import supybot.conf as conf
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
import supybot.utils as utils import supybot.utils as utils
import supybot.plugins as plugins
from supybot.commands import wrap
import supybot.ircutils as ircutils import supybot.ircutils as ircutils
import supybot.privmsgs as privmsgs import supybot.privmsgs as privmsgs
import supybot.callbacks as callbacks import supybot.callbacks as callbacks
class NewsRecord(dbi.Record): class DbiNewsDB(plugins.DbiChannelDB):
class DB(dbi.DB):
class Record(dbi.Record):
__fields__ = [ __fields__ = [
'subject', 'subject',
'text', 'text',
'at', 'at',
'expires', 'expires',
'by' 'by',
] ]
def __str__(self): def __str__(self):
format = conf.supybot.humanTimestampFormat() format = conf.supybot.humanTimestampFormat()
try: try:
@ -69,122 +72,63 @@ class NewsRecord(dbi.Record):
(self.text, self.subject, self.by, (self.text, self.subject, self.by,
time.strftime(format, time.localtime(int(self.at)))) time.strftime(format, time.localtime(int(self.at))))
else: else:
s = '%s (Subject: "%s", added by %s on %s, expires at %s)' % \ s = '%s (Subject: "%s", added by %s on %s, expires at %s)'
(self.text, self.subject, self.by, s = s % (self.text, self.subject, user,
time.strftime(format, time.localtime(int(self.at))), time.strftime(format, time.localtime(int(self.at))),
time.strftime(format, time.localtime(int(self.expires)))) time.strftime(format, time.localtime(int(self.expires))))
return s return s
class SqliteNewsDB(object):
def __init__(self, filename): def __init__(self, filename):
self.dbs = ircutils.IrcDict() # We use self.__class__ here because apparently DB isn't in our
self.filename = filename # scope. python--
self.__parent = super(self.__class__, self)
self.__parent.__init__(filename)
def close(self): def add(self, subject, text, at, expires, by):
for db in self.dbs.itervalues(): return self.__parent.add(self.Record(at=at, by=by, text=text,
db.close() subject=subject, expires=expires))
def _getDb(self, channel): def getOld(self, id=None):
try: now = time.time()
import sqlite
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to ' \
'use this plugin. Download it at ' \
'<http://pysqlite.sf.net/>'
filename = plugins.makeChannelFilename(self.filename, channel)
if filename in self.dbs:
return self.dbs[filename]
if os.path.exists(filename):
self.dbs[filename] = sqlite.connect(filename)
return self.dbs[filename]
db = sqlite.connect(filename)
self.dbs[filename] = db
cursor = db.cursor()
cursor.execute("""CREATE TABLE news (
id INTEGER PRIMARY KEY,
subject TEXT,
item TEXT,
added_at TIMESTAMP,
expires_at TIMESTAMP,
added_by TEXT
)""")
db.commit()
return db
def add(self, channel, subject, text, added_at, expires, by):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("INSERT INTO news VALUES (NULL, %s, %s, %s, %s, %s)",
subject, text, added_at, expires, by)
db.commit()
# XXX This should change only to support id, and the old functionality
# should move out to another method.
def get(self, channel, id=None, old=False):
db = self._getDb(channel)
cursor = db.cursor()
if id: if id:
cursor.execute("""SELECT item, subject, added_at, return self.get(id)
expires_at, added_by
FROM news
WHERE id=%s""", id)
if cursor.rowcount == 0:
raise dbi.NoRecordError, id
(text, subject, at, expires, by) = cursor.fetchone()
return NewsRecord(id, text=text, subject=subject, at=int(at),
expires=int(expires), by=by)
else: else:
if old: L = [R for R in self if R.expires < now and R.expires != 0]
cursor.execute("""SELECT id, subject if not L:
FROM news
WHERE expires_at <> 0 AND expires_at < %s
ORDER BY id DESC""", int(time.time()))
else:
cursor.execute("""SELECT id, subject
FROM news
WHERE expires_at > %s OR expires_at=0""",
int(time.time()))
if cursor.rowcount == 0:
raise dbi.NoRecordError raise dbi.NoRecordError
else: else:
return cursor.fetchall() return L
def remove(self, channel, id): def get(self, id=None):
db = self._getDb(channel) now = time.time()
cursor = db.cursor() if id:
cursor.execute("""DELETE FROM news WHERE id = %s""", id) return self.__parent.get(id)
db.commit() else:
if cursor.rowcount == 0: L = [R for R in self if R.expires >= now or R.expires == 0]
raise dbi.NoRecordError, id if not L:
raise dbi.NoRecordError
return L
def change(self, channel, id, replacer): def change(self, id, f):
db = self._getDb(channel) news = self.get(id)
cursor = db.cursor() s = '%s: %s' % (news.subject, news.text)
cursor.execute("""SELECT subject, item FROM news WHERE id=%s""", id) s = f(s)
if cursor.rowcount == 0: (news.subject, news.text) = s.split(': ', 1)
raise dbi.NoRecordError, id self.set(id, news)
(subject, item) = cursor.fetchone()
s = '%s: %s' % (subject, item)
s = replacer(s)
(newSubject, newItem) = s.split(': ', 1)
cursor.execute("""UPDATE news SET subject=%s, item=%s WHERE id=%s""",
newSubject, newItem, id)
NewsDB = plugins.DB('News', NewsDB = plugins.DB('News', {'flat': DbiNewsDB})
{'sqlite': SqliteNewsDB})
class News(callbacks.Privmsg): class News(callbacks.Privmsg):
def __init__(self): def __init__(self):
self.__parent = super(News, self) self.__parent = super(News, self)
self.__parent.__init__() self.__parent.__init__()
self.db = NewsDB() self.db = NewsDB()
self.removeOld = False
def die(self): def die(self):
self.__parent.die() self.__parent.die()
self.db.close() self.db.close()
def add(self, irc, msg, args, channel): def add(self, irc, msg, args, channel, user, at, expires, news):
"""[<channel>] <expires> <subject>: <text> """[<channel>] <expires> <subject>: <text>
Adds a given news item of <text> to a channel with the given <subject>. Adds a given news item of <text> to a channel with the given <subject>.
@ -192,24 +136,15 @@ class News(callbacks.Privmsg):
now. <channel> is only necessary if the message isn't sent in the now. <channel> is only necessary if the message isn't sent in the
channel itself. channel itself.
""" """
(expire_interval, news) = privmsgs.getArgs(args, required=2)
try: try:
expire_interval = int(expire_interval)
(subject, text) = news.split(': ', 1) (subject, text) = news.split(': ', 1)
except ValueError: except ValueError:
raise callbacks.ArgumentError raise callbacks.ArgumentError
added_at = int(time.time()) id = self.db.add(channel, subject, text, at, expires, user.id)
expires = expire_interval and (added_at + expire_interval) irc.replySuccess('(News item #%s added)' % id)
# Set the other stuff needed for the insert. add = wrap(add, ['channeldb', 'user', 'now', 'expiry', 'text'])
try:
by = ircdb.users.getUserId(msg.prefix)
except KeyError:
by = msg.nick
self.db.add(channel, subject, text, added_at, expires, by)
irc.replySuccess()
add = privmsgs.checkChannelCapability(add, 'news')
def news(self, irc, msg, args): def news(self, irc, msg, args, channel, id):
"""[<channel>] [<id>] """[<channel>] [<id>]
Display the news items for <channel> in the format of '(#id) subject'. Display the news items for <channel> in the format of '(#id) subject'.
@ -217,81 +152,92 @@ class News(callbacks.Privmsg):
news items. <channel> is only necessary if the message isn't sent in news items. <channel> is only necessary if the message isn't sent in
the channel itself. the channel itself.
""" """
channel = privmsgs.getChannel(msg, args) if not id:
id = privmsgs.getArgs(args, required=0, optional=1)
if id:
try:
record = self.db.get(channel, int(id))
irc.reply(str(record))
except dbi.NoRecordError, id:
irc.errorInvalid('news item id', id)
else:
try: try:
records = self.db.get(channel) records = self.db.get(channel)
items = ['(#%s) %s' % (id, s) for (id, s) in records] items = ['(#%s) %s' % (R.id, R.subject) for R in records]
s = 'News for %s: %s' % (channel, '; '.join(items)) s = 'News for %s: %s' % (channel, '; '.join(items))
irc.reply(s) irc.reply(s)
except dbi.NoRecordError: except dbi.NoRecordError:
irc.reply('No news for %s.' % channel) irc.reply('No news for %s.' % channel)
else:
def remove(self, irc, msg, args, channel):
"""[<channel>] <number>
Removes the news item with id <number> from <channel>. <channel> is
only necessary if the message isn't sent in the channel itself.
"""
id = privmsgs.getArgs(args)
try: try:
if id < 1:
raise ValueError
record = self.db.get(channel, id)
irc.reply(str(record))
except dbi.NoRecordError, id:
irc.errorInvalid('news item id', id)
except ValueError:
irc.errorInvalid('news item id', id,
'<id> must be a positive integer.')
news = wrap(news, ['channeldb', ('int?', None)])
def remove(self, irc, msg, args, channel, id):
"""[<channel>] <id>
Removes the news item with <id> from <channel>. <channel> is only
necessary if the message isn't sent in the channel itself.
"""
try:
if id < 1:
raise ValueError
self.db.remove(channel, id) self.db.remove(channel, id)
irc.replySuccess() irc.replySuccess()
except dbi.NoRecordError: except dbi.NoRecordError:
irc.errorInvalid('news item id', id) irc.errorInvalid('news item id', id)
remove = privmsgs.checkChannelCapability(remove, 'news') except ValueError:
irc.errorInvalid('news item id', id,
'<id> must be a positive integer.')
remove = wrap(remove, ['channeldb', 'int'])
def change(self, irc, msg, args, channel): def change(self, irc, msg, args, channel, id, replacer):
"""[<channel>] <number> <regexp> """[<channel>] <id> <regexp>
Changes the news item with id <number> from <channel> according to the Changes the news item with <id> from <channel> according to the
regular expression <regexp>. <regexp> should be of the form regular expression <regexp>. <regexp> should be of the form
s/text/replacement/flags. <channel> is only necessary if the message s/text/replacement/flags. <channel> is only necessary if the message
isn't sent on the channel itself. isn't sent on the channel itself.
""" """
(id, regexp) = privmsgs.getArgs(args, required=2)
try:
replacer = utils.perlReToReplacer(regexp)
except ValueError, e:
irc.error(str(e))
return
try: try:
if id < 1:
raise ValueError
self.db.change(channel, id, replacer) self.db.change(channel, id, replacer)
irc.replySuccess() irc.replySuccess()
except dbi.NoRecordError: except dbi.NoRecordError:
irc.errorInvalid('news item id', id) irc.errorInvalid('news item id', id)
change = privmsgs.checkChannelCapability(change, 'news') except ValueError:
irc.errorInvalid('news item id', id,
'<id> must be a positive integer.')
change = wrap(change, ['channeldb', 'int', 'regexpReplacer'])
def old(self, irc, msg, args): def old(self, irc, msg, args, channel, id):
"""[<channel>] [<number>] """[<channel>] [<id>]
Returns the old news item for <channel> with id <number>. If no number Returns the old news item for <channel> with <id>. If no number is
is given, returns all the old news items in reverse order. <channel> given, returns all the old news items in reverse order. <channel> is
is only necessary if the message isn't sent in the channel itself. only necessary if the message isn't sent in the channel itself.
""" """
channel = privmsgs.getChannel(msg, args)
id = privmsgs.getArgs(args, required=0, optional=1)
if id: if id:
try: try:
record = self.db.get(channel, id, old=True) if id < 1:
raise ValueError
record = self.db.getOld(channel, id)
irc.reply(str(record)) irc.reply(str(record))
except dbi.NoRecordError, id: except dbi.NoRecordError, id:
irc.errorInvalid('news item id', id) irc.errorInvalid('news item id', id)
except ValueError:
irc.errorInvalid('news item id', id,
'<id> must be a positive integer.')
else: else:
try: try:
records = self.db.get(channel, old=True) records = self.db.getOld(channel)
items = ['(#%s) %s' % (id, s) for (id, s) in records] items = ['(#%s) %s' % (R.id, R.subject) for R in records]
s = 'Old news for %s: %s' % (channel, '; '.join(items)) s = 'Old news for %s: %s' % (channel, '; '.join(items))
irc.reply(s) irc.reply(s)
except dbi.NoRecordError: except dbi.NoRecordError:
irc.reply('No old news for %s.' % channel) irc.reply('No old news for %s.' % channel)
old = wrap(old, ['channeldb', ('int?', None)])
Class = News Class = News

View File

@ -33,15 +33,16 @@ import time
import supybot.utils as utils import supybot.utils as utils
try: class NewsTestCase(ChannelPluginTestCase):
import sqlite plugins = ('News','User')
except ImportError: def setUp(self):
sqlite = None ChannelPluginTestCase.setUp(self)
# Create a valid user to use
self.prefix = 'news!bar@baz'
self.irc.feedMsg(ircmsgs.privmsg(self.nick, 'register tester moo',
prefix=self.prefix))
m = self.irc.takeMsg() # Response to register.
if sqlite is not None:
class NewsTestCase(ChannelPluginTestCase):
plugins = ('News',)
def testAddnews(self): def testAddnews(self):
self.assertNotError('add 0 subject: foo') self.assertNotError('add 0 subject: foo')
self.assertRegexp('news', 'subject') self.assertRegexp('news', 'subject')
@ -84,6 +85,5 @@ if sqlite is not None:
print 'Done sleeping.' print 'Done sleeping.'
self.assertNotError('old') self.assertNotError('old')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: # vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: