mirror of
https://github.com/Mikaela/Limnoria.git
synced 2025-01-11 12:42:34 +01:00
Wrote tests and fixed lots of bugs and usability flaws.
This commit is contained in:
parent
22fcd451e8
commit
60c598be87
@ -32,15 +32,6 @@
|
||||
"""
|
||||
Handles "factoids," little tidbits of information held in a database and
|
||||
available on demand via several commands.
|
||||
|
||||
Commands include:
|
||||
addfactoid
|
||||
removefactoid
|
||||
lookupfactoid
|
||||
lockfactoid
|
||||
unlockfactoid
|
||||
randomfactoid
|
||||
factoidinfo
|
||||
"""
|
||||
|
||||
from baseplugin import *
|
||||
@ -86,16 +77,20 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
|
||||
db.commit()
|
||||
return db
|
||||
|
||||
def add(self, irc, msg, args):
|
||||
def learn(self, irc, msg, args):
|
||||
"""[<channel>] <key> as <value>
|
||||
|
||||
Associates <key> with <value>. <channel> is only necessary if the
|
||||
message isn't sent on the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
(key, as, factoid) = privmsgs.getArgs(args, needed=3)
|
||||
if as != 'as':
|
||||
try:
|
||||
i = args.index('as')
|
||||
except ValueError:
|
||||
raise callbacks.ArgumentError
|
||||
args.pop(i)
|
||||
key = ' '.join(args[:i])
|
||||
factoid = ' '.join(args[i:])
|
||||
db = self.getDb(channel)
|
||||
cursor = db.cursor()
|
||||
cursor.execute("""SELECT id, locked FROM keys WHERE key=%s""", key)
|
||||
@ -121,29 +116,44 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
|
||||
else:
|
||||
irc.error(msg, 'That factoid is locked.')
|
||||
|
||||
def lookup(self, irc, msg, args):
|
||||
"[<channel>] (If not sent in the channel itself) <key> [<number>]"
|
||||
def whatis(self, irc, msg, args):
|
||||
"""[<channel>] <key> [<number>]
|
||||
|
||||
Looks up the value of <key> in the factoid database. If given a
|
||||
number, will return only that exact factoid. <channel> is only
|
||||
necessary if the message isn't sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
(key, number) = privmsgs.getArgs(args, optional=1)
|
||||
try:
|
||||
number = int(number)
|
||||
except ValueError:
|
||||
key += number
|
||||
number = 0
|
||||
key = privmsgs.getArgs(args)
|
||||
db = self.getDb(channel)
|
||||
cursor = db.cursor()
|
||||
cursor.execute("""SELECT factoids.fact FROM factoids, keys WHERE
|
||||
keys.key=%s AND factoids.key_id=keys.id
|
||||
ORDER BY factoids.id""", key)
|
||||
results = cursor.fetchall()
|
||||
if len(results) == 0:
|
||||
ORDER BY factoids.id
|
||||
LIMIT 20""", key)
|
||||
if cursor.rowcount == 0:
|
||||
irc.error(msg, 'No factoid matches that key.')
|
||||
else:
|
||||
factoid = results[number][0]
|
||||
irc.reply(msg, '%s/%s: %s' % (key, number, factoid))
|
||||
counter = 0
|
||||
factoids = []
|
||||
for result in cursor.fetchall():
|
||||
factoids.append('(#%s) %s' % (counter, result[0]))
|
||||
counter += 1
|
||||
totalResults = len(factoids)
|
||||
if ircutils.shrinkList(factoids, ', or ', 400):
|
||||
s = '%s could be %s. (%s results shown out of %s)' % \
|
||||
(key, ', or '.join(factoids), counter-1, totalResults)
|
||||
else:
|
||||
s = '%s could be %s.' % (key, ', or '.join(factoids))
|
||||
irc.reply(msg, s)
|
||||
|
||||
def lock(self, irc, msg, args):
|
||||
"[<channel>] (If not sent in the channel itself) <key>"
|
||||
"""[<channel>] <key>
|
||||
|
||||
Locks the factoid(s) associated with <key> so that they cannot be
|
||||
removed or added to. <channel> is only necessary if the message isn't
|
||||
sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
key = privmsgs.getArgs(args)
|
||||
db = self.getDb(channel)
|
||||
@ -157,7 +167,12 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
|
||||
irc.error(msg, conf.replyNoCapability % capability)
|
||||
|
||||
def unlock(self, irc, msg, args):
|
||||
"[<channel>] (If not sent in the channel itself) <key>"
|
||||
"""[<channel>] <key>
|
||||
|
||||
Unlocks the factoid(s) associated with <key> so that they can be
|
||||
removed or added to. <channel> is only necessary if the message isn't
|
||||
sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
key = privmsgs.getArgs(args)
|
||||
db = self.getDb(channel)
|
||||
@ -170,22 +185,60 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
|
||||
else:
|
||||
irc.error(msg, conf.replyNoCapability % capability)
|
||||
|
||||
def remove(self, irc, msg, args):
|
||||
"[<channel>] (If not sent in the channel itself) <key>"
|
||||
def unlearn(self, irc, msg, args):
|
||||
"""[<channel>] <key> [<number>]
|
||||
|
||||
Removes the factoid <key> from the factoids database. If there are
|
||||
more than one factoid with such a key, a number is necessary to
|
||||
determine which one should be removed. <channel> is only necessary if
|
||||
the message isn't sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
if args[-1].isdigit:
|
||||
number = int(args.pop())
|
||||
else:
|
||||
number = None
|
||||
key = privmsgs.getArgs(args)
|
||||
db = self.getDb(channel)
|
||||
capability = ircdb.makeChannelCapability(channel, 'factoids')
|
||||
if ircdb.checkCapability(msg.prefix, capability):
|
||||
cursor = db.cursor()
|
||||
cursor.execute("""DELETE FROM keys WHERE key=%s""", key)
|
||||
db.commit()
|
||||
irc.reply(msg, conf.replySuccess)
|
||||
cursor.execute("""SELECT keys.id, factoids.id
|
||||
FROM keys, factoids
|
||||
WHERE key=%s AND
|
||||
factoids.key_id=keys.id""", key)
|
||||
if cursor.rowcount == 0:
|
||||
irc.error(msg, 'There is no such factoid.')
|
||||
elif cursor.rowcount == 1:
|
||||
(id, _) = cursor.fetchone()
|
||||
cursor.execute("""DELETE FROM factoids WHERE key_id=%s""", id)
|
||||
cursor.execute("""DELETE FROM keys WHERE key=%s""", key)
|
||||
db.commit()
|
||||
irc.reply(msg, conf.replySuccess)
|
||||
else:
|
||||
if number is not None:
|
||||
results = cursor.fetchall()
|
||||
try:
|
||||
(_, id) = results[number]
|
||||
except IndexError:
|
||||
irc.error(msg, 'Invalid factoid number.')
|
||||
return
|
||||
cursor.execute("DELETE FROM factoids WHERE id=%s", id)
|
||||
db.commit()
|
||||
irc.reply(msg, conf.replySuccess)
|
||||
else:
|
||||
irc.error(msg, '%s factoids have that key. ' \
|
||||
'Please specify which one to remove.' % \
|
||||
cursor.rowcount)
|
||||
else:
|
||||
irc.error(msg, conf.replyNoCapability % capability)
|
||||
|
||||
def randomfactoid(self, irc, msg, args):
|
||||
"[<channel>] (If not sent in the channel itself)"
|
||||
"""[<channel>]
|
||||
|
||||
Returns a random factoid from the database for <channel>. <channel>
|
||||
is only necessary if the message isn't sent in the channel itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
db = self.getDb(channel)
|
||||
cursor = db.cursor()
|
||||
@ -201,7 +254,12 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
|
||||
irc.error(msg, 'I couldn\'t find a factoid.')
|
||||
|
||||
def factoidinfo(self, irc, msg, args):
|
||||
"[<channel>] (If not sent in the channel itself) <key>"
|
||||
"""[<channel>] <key>
|
||||
|
||||
Gives information about the factoid(s) associated with <key>.
|
||||
<channel> is only necessary if the message isn't sent in the channel
|
||||
itself.
|
||||
"""
|
||||
channel = privmsgs.getChannel(msg, args)
|
||||
key = privmsgs.getArgs(args)
|
||||
db = self.getDb(channel)
|
||||
@ -224,7 +282,7 @@ class Factoids(ChannelDBHandler, callbacks.Privmsg):
|
||||
counter += 1
|
||||
factoids = '; '.join(L)
|
||||
s = 'Key %r is %s and has %s factoids associated with it: %s' % \
|
||||
(key, locked and 'locked' or 'not locked', counter, '; '.join(L))
|
||||
(key, locked and 'locked' or 'not locked', counter, factoids)
|
||||
irc.reply(msg, s)
|
||||
|
||||
|
||||
|
70
test/test_Factoids.py
Normal file
70
test/test_Factoids.py
Normal file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
###
|
||||
# Copyright (c) 2002, Jeremiah Fincher
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author of this software nor the name of
|
||||
# contributors to this software may be used to endorse or promote products
|
||||
# derived from this software without specific prior written consent.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
###
|
||||
|
||||
from test import *
|
||||
|
||||
class FactoidsTestCase(ChannelPluginTestCase):
|
||||
plugins = ('Factoids',)
|
||||
def testRandomfactoid(self):
|
||||
self.assertError('randomfactoid')
|
||||
self.assertNotError('learn jemfinch as my primary author')
|
||||
self.assertRegexp('randomfactoid', 'primary author')
|
||||
|
||||
def testLearn(self):
|
||||
self.assertNotError('learn jemfinch as my primary author')
|
||||
self.assertNotError('factoidinfo jemfinch')
|
||||
self.assertRegexp('whatis jemfinch', 'my primary author')
|
||||
self.assertNotError('learn jemfinch as a crappy assembly programmer')
|
||||
self.assertRegexp('whatis jemfinch', r'.*primary author.*assembly')
|
||||
self.assertError('unlearn jemfinch')
|
||||
self.assertError('unlearn jemfinch 2')
|
||||
self.assertNotError('unlearn jemfinch 1')
|
||||
self.assertNotError('unlearn jemfinch 0')
|
||||
self.assertError('whatis jemfinch')
|
||||
self.assertError('factoidinfo jemfinch')
|
||||
|
||||
self.assertNotError('learn foo bar as baz')
|
||||
self.assertNotError('factoidinfo foo bar')
|
||||
self.assertRegexp('whatis foo bar', 'baz')
|
||||
self.assertNotError('learn foo bar as quux')
|
||||
self.assertRegexp('whatis foo bar', '.*baz.*quux')
|
||||
self.assertError('unlearn foo bar')
|
||||
self.assertNotError('unlearn foo bar 1')
|
||||
self.assertNotError('unlearn foo bar 0')
|
||||
self.assertError('whatis foo bar')
|
||||
self.assertError('factoidinfo foo bar')
|
||||
|
||||
self.assertRegexp('learn foo bar baz', '^learn') # No 'as'
|
||||
self.assertRegexp('learn foo bar', '^learn') # No 'as'
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|
||||
|
Loading…
Reference in New Issue
Block a user