Converted to use dbi.

This commit is contained in:
Jeremy Fincher 2004-08-11 06:17:11 +00:00
parent 77ce14f481
commit 0768ce1c85
3 changed files with 180 additions and 386 deletions

View File

@ -43,6 +43,7 @@ import sets
import random import random
import itertools import itertools
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
@ -53,49 +54,14 @@ import supybot.privmsgs as privmsgs
import supybot.registry as registry import supybot.registry as registry
import supybot.callbacks as callbacks import supybot.callbacks as callbacks
class DbiFunDBDB(object):
class FunDBDBInterface(object): class FunDBDB(dbi.DB):
def close(self): class Record(object):
pass __metaclass__ = dbi.Record
__fields__ = [
def flush(self): 'by',
pass ('text', str),
]
def get(self, channel, type, id):
"""Returns just the text associated with the channel, type, and id."""
raise NotImplementedError
def info(self, channel, type, id):
"""Returns the test and the metadata associated with the
channel, type, and id."""
raise NotImplementedError
def add(self, channel, type, text, by):
raise NotImplementedError
def remove(self, channel, type, id):
raise NotImplementedError
def change(self, channel, type, id, f):
raise NotImplementedError
def random(self, channel, type):
raise NotImplementedError
def size(self, channel, type):
raise NotImplementedError
def search(self, channel, type, p):
"""Returns a list of (id, text) pairs whose text matches predicate p"""
raise NotImplementedError
class FlatfileFunDBDB(FunDBDBInterface):
class FunDBDB(plugins.FlatfileDB):
def serialize(self, v):
return csv.join(map(str, v))
def deserialize(self, s):
return csv.split(s)
def __init__(self): def __init__(self):
self.dbs = ircutils.IrcDict() self.dbs = ircutils.IrcDict()
@ -121,49 +87,33 @@ class FlatfileFunDBDB(FunDBDBInterface):
return self.dbs[channel][type] return self.dbs[channel][type]
def get(self, channel, type, id): def get(self, channel, type, id):
return self.info(channel, type, id)[1]
def info(self, channel, type, id):
db = self._getDb(channel, type) db = self._getDb(channel, type)
return db.getRecord(id) return db.get(id)
def add(self, channel, type, text, by): def add(self, channel, type, text, by):
db = self._getDb(channel, type) db = self._getDb(channel, type)
return db.addRecord([by, text]) return db.add(db.Record(by=by, text=text))
def remove(self, channel, type, id): def remove(self, channel, type, id):
db = self._getDb(channel, type) db = self._getDb(channel, type)
db.delRecord(id) db.remove(id)
def change(self, channel, type, id, f): def change(self, channel, type, id, f):
db = self._getDb(channel, type) db = self._getDb(channel, type)
(by, text) = db.getRecord(id) record = db.get(id)
db.setRecord(id, [by, f(text)]) record.text = f(record.text)
db.set(id, record)
def random(self, channel, type): def random(self, channel, type):
db = self._getDb(channel, type) db = self._getDb(channel, type)
t = random.choice(db.records()) return db.random()
if t is not None:
(id, (by, text)) = t
t = (id, text)
return t
def size(self, channel, type): def size(self, channel, type):
db = self._getDb(channel, type) db = self._getDb(channel, type)
return itertools.ilen(db.records()) return itertools.ilen(db)
def search(self, channel, type, p):
db = self._getDb(channel, type)
L = []
for (id, record) in db.records():
text = record[1]
if p(text):
L.append((id, text))
return L
def FunDBDB(): def FunDBDB():
return FlatfileFunDBDB() return DbiFunDBDB()
conf.registerPlugin('FunDB') conf.registerPlugin('FunDB')
conf.registerChannelValue(conf.supybot.plugins.FunDB, 'showIds', conf.registerChannelValue(conf.supybot.plugins.FunDB, 'showIds',
@ -316,8 +266,8 @@ class FunDB(callbacks.Privmsg):
if id is None: if id is None:
return return
try: try:
text = self.db.get(channel, type, id) x = self.db.get(channel, type, id)
irc.reply(text) irc.reply(x.text)
except KeyError: except KeyError:
irc.error('There is no %s with that id.' % type) irc.error('There is no %s with that id.' % type)
@ -336,8 +286,8 @@ class FunDB(callbacks.Privmsg):
if id is None: if id is None:
return return
try: try:
(text, by) = self.db.info(channel, type, id) x = self.db.get(channel, type, id)
reply = '%s #%s: %r; Created by %s.' % (type, id, text, by) reply = '%s #%s: %r; Created by %s.' % (type, x.id, x.text, x.by)
irc.reply(reply) irc.reply(reply)
except KeyError: except KeyError:
irc.error('There is no %s with that id.' % type) irc.error('There is no %s with that id.' % type)
@ -365,14 +315,13 @@ class FunDB(callbacks.Privmsg):
nick = privmsgs.getArgs(args) nick = privmsgs.getArgs(args)
if not nick: if not nick:
raise callbacks.ArgumentError raise callbacks.ArgumentError
t = self.db.random(channel, 'insult') insult = self.db.random(channel, 'insult')
if t is None: if insult is None:
irc.error('There are currently no available insults.') irc.error('There are currently no available insults.')
else: else:
(id, insult) = t
nick = self._replaceFirstPerson(nick, msg.nick) nick = self._replaceFirstPerson(nick, msg.nick)
insult = '%s: %s' % (nick, insult.replace('$who', nick)) s = '%s: %s' % (nick, insult.text.replace('$who', nick))
irc.reply(self._formatResponse(insult, id, channel), irc.reply(self._formatResponse(s, insult.id, channel),
prefixName=False) prefixName=False)
def lart(self, irc, msg, args): def lart(self, irc, msg, args):
@ -400,23 +349,21 @@ class FunDB(callbacks.Privmsg):
if id: if id:
try: try:
lart = self.db.get(channel, 'lart', id) lart = self.db.get(channel, 'lart', id)
t = (id, lart)
except KeyError: except KeyError:
irc.error('There is no such lart.') irc.error('There is no such lart.')
return return
else: else:
t = self.db.random(channel, 'lart') lart = self.db.random(channel, 'lart')
if t is None: if lart is None:
irc.error('There are currently no available larts.') irc.error('There are currently no available larts.')
else: else:
(id, lart) = t
nick = self._replaceFirstPerson(nick, msg.nick) nick = self._replaceFirstPerson(nick, msg.nick)
reason = self._replaceFirstPerson(reason, msg.nick) reason = self._replaceFirstPerson(reason, msg.nick)
s = lart.replace('$who', nick) s = lart.text.replace('$who', nick)
if reason: if reason:
s = '%s for %s' % (s, reason) s = '%s for %s' % (s, reason)
s = s.rstrip('.') s = s.rstrip('.')
irc.reply(self._formatResponse(s, id, channel), action=True) irc.reply(self._formatResponse(s, lart.id, channel), action=True)
def praise(self, irc, msg, args): def praise(self, irc, msg, args):
"""[<channel>] [<id>] <text> [for <reason>] """[<channel>] [<id>] <text> [for <reason>]
@ -442,23 +389,21 @@ class FunDB(callbacks.Privmsg):
if id: if id:
try: try:
praise = self.db.get(channel, 'praise', id) praise = self.db.get(channel, 'praise', id)
t = (id, praise)
except KeyError: except KeyError:
irc.error('There is no such praise.') irc.error('There is no such praise.')
return return
else: else:
t = self.db.random(channel, 'praise') praise = self.db.random(channel, 'praise')
if t is None: if praise is None:
irc.error('There are currently no available praises.') irc.error('There are currently no available praises.')
else: else:
(id, praise) = t
nick = self._replaceFirstPerson(nick, msg.nick) nick = self._replaceFirstPerson(nick, msg.nick)
reason = self._replaceFirstPerson(reason, msg.nick) reason = self._replaceFirstPerson(reason, msg.nick)
s = praise.replace('$who', nick) s = praise.text.replace('$who', nick)
if reason: if reason:
s = '%s for %s' % (s, reason) s = '%s for %s' % (s, reason)
s = s.rstrip('.') s = s.rstrip('.')
irc.reply(self._formatResponse(s, id, channel), action=True) irc.reply(self._formatResponse(s, praise.id, channel), action=True)
Class = FunDB Class = FunDB

View File

@ -373,136 +373,6 @@ class PeriodicFileDownloader(object):
world.threadsSpawned += 1 world.threadsSpawned += 1
class FlatfileDB(object):
def __init__(self, filename, maxSize=10**6):
self.filename = filename
try:
fd = file(self.filename)
strId = fd.readline().rstrip()
self.maxSize = len(strId)
self.currentId = int(strId)
except EnvironmentError, e:
# File couldn't be opened.
self.maxSize = int(math.log10(maxSize))
self.currentId = 0
self._incrementCurrentId()
def serialize(self, record):
raise NotImplementedError
def deserialize(self, s):
raise NotImplementedError
def _canonicalId(self, id):
if id is not None:
return str(id).zfill(self.maxSize)
else:
return '-'*self.maxSize
def _incrementCurrentId(self, fd=None):
fdWasNone = fd is None
if fdWasNone:
fd = file(self.filename, 'a')
fd.seek(0)
self.currentId += 1
fd.write(self._canonicalId(self.currentId))
fd.write('\n')
if fdWasNone:
fd.close()
def _splitLine(self, line):
line = line.rstrip('\r\n')
(strId, strRecord) = line.split(':', 1)
return (strId, strRecord)
def _joinLine(self, id, record):
return '%s:%s\n' % (self._canonicalId(id), self.serialize(record))
def addRecord(self, record):
line = self._joinLine(self.currentId, record)
try:
fd = file(self.filename, 'r+')
fd.seek(0, 2) # End.
fd.write(line)
return self.currentId
finally:
self._incrementCurrentId(fd)
fd.close()
def getRecord(self, id):
strId = self._canonicalId(id)
try:
fd = file(self.filename)
fd.readline() # First line, nextId.
for line in fd:
(lineId, strRecord) = self._splitLine(line)
if lineId == strId:
return self.deserialize(strRecord)
raise KeyError, id
finally:
fd.close()
def setRecord(self, id, record):
strLine = self._joinLine(id, record)
try:
fd = file(self.filename, 'r+')
self.delRecord(id, fd)
fd.seek(0, 2) # End.
fd.write(strLine)
finally:
fd.close()
def delRecord(self, id, fd=None):
fdWasNone = fd is None
strId = self._canonicalId(id)
try:
if fdWasNone:
fd = file(self.filename, 'r+')
fd.seek(0)
fd.readline() # First line, nextId
pos = fd.tell()
line = fd.readline()
while line:
(lineId, strRecord) = self._splitLine(line)
if lineId == strId:
fd.seek(pos)
fd.write(self._canonicalId(None))
fd.seek(pos)
fd.readline() # Same line we just rewrote the id for.
pos = fd.tell()
line = fd.readline()
# We should be at the end.
finally:
if fdWasNone:
fd.close()
def records(self):
fd = file(self.filename)
fd.readline() # First line, nextId.
for line in fd:
(strId, strRecord) = self._splitLine(line)
if not strId.startswith('-'):
yield (int(strId), self.deserialize(strRecord))
fd.close()
def vacuum(self):
infd = file(self.filename)
outfd = utils.transactionalFile(self.filename)
outfd.write(infd.readline()) # First line, nextId.
for line in infd:
if not line.startswith('-'):
outfd.write(line)
infd.close()
outfd.close()
def flush(self):
pass # No-op, we maintain no open files.
def close(self):
self.vacuum() # Should we do this? It should be fine.
_randomnickRe = re.compile(r'\$rand(?:om)?nick', re.I) _randomnickRe = re.compile(r'\$rand(?:om)?nick', re.I)
_randomdateRe = re.compile(r'\$rand(?:om)?date', re.I) _randomdateRe = re.compile(r'\$rand(?:om)?date', re.I)
_randomintRe = re.compile(r'\$rand(?:omint)?', re.I) _randomintRe = re.compile(r'\$rand(?:omint)?', re.I)

View File

@ -33,12 +33,6 @@ from testsupport import *
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
try:
import sqlite
except ImportError:
sqlite = None
if sqlite is not None:
class TestFunDB(ChannelPluginTestCase, PluginDocumentation): class TestFunDB(ChannelPluginTestCase, PluginDocumentation):
plugins = ('FunDB','User','Utilities') plugins = ('FunDB','User','Utilities')
def setUp(self): def setUp(self):
@ -64,24 +58,19 @@ if sqlite is not None:
def testLart(self): def testLart(self):
self.assertNotError('add lart jabs $who') self.assertNotError('add lart jabs $who')
self.assertHelp('lart') self.assertHelp('lart')
self.assertResponse('lart jemfinch for being dumb', self.assertAction('lart jemfinch for being dumb',
'\x01ACTION jabs jemfinch for being dumb ' 'jabs jemfinch for being dumb (#1)')
'(#1)\x01') self.assertAction('lart jemfinch', 'jabs jemfinch (#1)')
self.assertResponse('lart jemfinch',
'\x01ACTION jabs jemfinch (#1)\x01')
self.assertRegexp('stats lart', 'currently 1 lart') self.assertRegexp('stats lart', 'currently 1 lart')
self.assertNotError('add lart shoots $who') self.assertNotError('add lart shoots $who')
self.assertHelp('lart 1') self.assertHelp('lart 1')
self.assertResponse('lart 1 jemfinch', self.assertAction('lart 1 jemfinch', 'jabs jemfinch (#1)')
'\x01ACTION jabs jemfinch (#1)\x01') self.assertAction('lart 2 jemfinch for being dumb',
self.assertResponse('lart 2 jemfinch for being dumb', 'shoots jemfinch for being dumb (#2)')
'\x01ACTION shoots jemfinch for being dumb '
'(#2)\x01')
self.assertNotRegexp('lart %s' % self.irc.nick, self.irc.nick) self.assertNotRegexp('lart %s' % self.irc.nick, self.irc.nick)
self.assertNotError('remove lart 1') self.assertNotError('remove lart 1')
self.assertRegexp('stats lart', 'currently 1 lart') self.assertRegexp('stats lart', 'currently 1 lart')
self.assertResponse('lart jemfinch', self.assertAction('lart jemfinch', 'shoots jemfinch (#2)')
'\x01ACTION shoots jemfinch (#2)\x01')
self.assertNotError('remove lart 2') self.assertNotError('remove lart 2')
self.assertRegexp('stats lart', 'currently 0') self.assertRegexp('stats lart', 'currently 0')
self.assertError('lart jemfinch') self.assertError('lart jemfinch')
@ -138,14 +127,10 @@ if sqlite is not None:
self.assertNotError('add lart stubs $who') self.assertNotError('add lart stubs $who')
self.assertNotError('add #tester insult nimrod') self.assertNotError('add #tester insult nimrod')
self.assertNotError('add insult nimwit') self.assertNotError('add insult nimwit')
self.assertResponse('praise jemfinch', self.assertAction('praise jemfinch', 'pats jemfinch (#1)')
'\x01ACTION pats jemfinch (#1)\x01') self.assertAction('praise #tester jemfinch', 'pets jemfinch (#1)')
self.assertResponse('praise #tester jemfinch', self.assertAction('lart jemfinch', 'stubs jemfinch (#1)')
'\x01ACTION pets jemfinch (#1)\x01') self.assertAction('lart #tester jemfinch', 'stabs jemfinch (#1)')
self.assertResponse('lart jemfinch',
'\x01ACTION stubs jemfinch (#1)\x01')
self.assertResponse('lart #tester jemfinch',
'\x01ACTION stabs jemfinch (#1)\x01')
self.assertResponse('insult jemfinch', 'jemfinch: nimwit (#1)') self.assertResponse('insult jemfinch', 'jemfinch: nimwit (#1)')
self.assertResponse('insult #tester jemfinch', self.assertResponse('insult #tester jemfinch',
'jemfinch: nimrod (#1)') 'jemfinch: nimrod (#1)')
@ -153,23 +138,18 @@ if sqlite is not None:
def testPraise(self): def testPraise(self):
self.assertNotError('add praise pets $who') self.assertNotError('add praise pets $who')
self.assertHelp('praise') self.assertHelp('praise')
self.assertResponse('praise jemfinch for being him', self.assertAction('praise jemfinch for being him',
'\x01ACTION pets jemfinch for being him ' 'pets jemfinch for being him (#1)')
'(#1)\x01') self.assertAction('praise jemfinch', 'pets jemfinch (#1)')
self.assertResponse('praise jemfinch',
'\x01ACTION pets jemfinch (#1)\x01')
self.assertRegexp('stats praise', r'currently 1') self.assertRegexp('stats praise', r'currently 1')
self.assertNotError('add praise gives $who a cookie') self.assertNotError('add praise gives $who a cookie')
self.assertHelp('praise 1') self.assertHelp('praise 1')
self.assertResponse('praise 1 jemfinch', self.assertAction('praise 1 jemfinch', 'pets jemfinch (#1)')
'\x01ACTION pets jemfinch (#1)\x01') self.assertAction('praise 2 jemfinch for being him',
self.assertResponse('praise 2 jemfinch for being him', 'gives jemfinch a cookie for being him (#2)')
'\x01ACTION gives jemfinch a cookie for being '
'him (#2)\x01')
self.assertNotError('remove praise 1') self.assertNotError('remove praise 1')
self.assertRegexp('stats praise', r'currently 1') self.assertRegexp('stats praise', r'currently 1')
self.assertResponse('praise jemfinch', self.assertAction('praise jemfinch', 'gives jemfinch a cookie (#2)')
'\x01ACTION gives jemfinch a cookie (#2)\x01')
self.assertNotError('remove praise 2') self.assertNotError('remove praise 2')
self.assertRegexp('stats praise', r'currently 0') self.assertRegexp('stats praise', r'currently 0')
self.assertError('praise jemfinch') self.assertError('praise jemfinch')
@ -198,8 +178,7 @@ if sqlite is not None:
def testChange(self): def testChange(self):
self.assertNotError('add praise teaches $who perl') self.assertNotError('add praise teaches $who perl')
self.assertNotError('change praise 1 s/perl/python/') self.assertNotError('change praise 1 s/perl/python/')
self.assertResponse('praise jemfinch', self.assertAction('praise jemfinch', 'teaches jemfinch python (#1)')
'\x01ACTION teaches jemfinch python (#1)\x01')
self.assertNotError('remove praise 1') self.assertNotError('remove praise 1')
def testConfig(self): def testConfig(self):