Limnoria/plugins/Todo.py

359 lines
13 KiB
Python
Raw Normal View History

#!/usr/bin/env python
###
# Copyright (c) 2003, Daniel DiPaolo
# 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.
###
"""
2004-08-19 01:15:27 +02:00
The Todo plugin allows registered users to keep their own personal list of
tasks to do, with an optional priority for each.
"""
import supybot
2003-11-25 09:23:47 +01:00
__revision__ = "$Id$"
__author__ = supybot.authors.strike
2004-07-24 07:18:26 +02:00
import supybot.plugins as plugins
import glob
import time
import getopt
import string
import os.path
2004-07-24 07:18:26 +02:00
import supybot.conf as conf
import supybot.ircdb as ircdb
import supybot.utils as utils
import supybot.privmsgs as privmsgs
import supybot.callbacks as callbacks
try:
import sqlite
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
'plugin. Download it at <http://pysqlite.sf.net/>'
2003-12-08 14:08:14 +01:00
class TodoDB(plugins.DBHandler):
def makeDb(self, filename):
"""create Todo database and tables"""
if os.path.exists(filename):
db = sqlite.connect(filename)
else:
db = sqlite.connect(filename, converters={'bool': bool})
cursor = db.cursor()
cursor.execute("""CREATE TABLE todo (
id INTEGER PRIMARY KEY,
priority INTEGER,
added_at TIMESTAMP,
userid INTEGER,
task TEXT,
active BOOLEAN
)""")
db.commit()
return db
2004-07-21 21:36:35 +02:00
class Todo(callbacks.Privmsg):
def __init__(self):
callbacks.Privmsg.__init__(self)
2004-01-18 08:58:26 +01:00
dataDir = conf.supybot.directories.data()
self.dbHandler = TodoDB(os.path.join(dataDir, 'Todo'))
def die(self):
2003-12-08 14:08:14 +01:00
self.dbHandler.die()
callbacks.Privmsg.die(self)
2003-12-07 06:33:31 +01:00
def _shrink(self, s):
return utils.ellipsisify(s, 50)
def todo(self, irc, msg, args):
"""[<username>|<task id>]
Retrieves a task for the given task id. If no task id is given, it
will return a list of task ids that that user has added to their todo
list.
"""
arg = privmsgs.getArgs(args, required=0, optional=1)
userid = None
taskid = None
# Figure out what userid and taskid need to be set to (if anything)
if not arg:
pass
else:
try:
taskid = int(arg)
except ValueError:
try:
userid = ircdb.users.getUserId(arg)
except KeyError:
2004-09-17 07:09:14 +02:00
irc.error(
'%r is not a valid task id or username' % arg)
return
2003-12-08 14:08:14 +01:00
db = self.dbHandler.getDb()
cursor = db.cursor()
if not userid and not taskid:
try:
id = ircdb.users.getUserId(msg.prefix)
except KeyError:
2004-01-08 22:49:10 +01:00
irc.errorNotRegistered()
return
cursor.execute("""SELECT id, task FROM todo
WHERE userid = %s AND active = 1
ORDER BY priority, id""", id)
if cursor.rowcount == 0:
irc.reply('You have no tasks in your todo list.')
2003-12-07 06:33:31 +01:00
else:
L = ['#%s: %s' % (item[0], self._shrink(item[1]))
for item in cursor.fetchall()]
irc.reply(utils.commaAndify(L))
else:
if userid:
cursor.execute("""SELECT id, task FROM todo
WHERE userid = %s AND active = 1
ORDER BY priority, id""", userid)
if cursor.rowcount == 0:
irc.reply('That user has no todos.')
return
2003-12-07 06:33:31 +01:00
L = ['#%s: %s' % (item[0], self._shrink(item[1]))
2004-07-21 21:36:35 +02:00
for item in cursor.fetchall()]
2003-12-07 06:33:31 +01:00
if len(L) == 1:
s = 'Todo for %s: %s' % (arg, L[0])
else:
s = 'Todos for %s: %s' % (arg, utils.commaAndify(L))
irc.reply(s)
else:
2003-12-08 14:15:00 +01:00
cursor.execute("""SELECT userid,priority,added_at,task,active
FROM todo WHERE id = %s""", taskid)
if cursor.rowcount == 0:
2004-09-17 07:09:14 +02:00
irc.error('%r is not a valid task id' % taskid)
return
2003-12-08 14:15:00 +01:00
(userid, pri, added_at, task, active) = cursor.fetchone()
# Construct and return the reply
2003-12-08 14:15:00 +01:00
user = ircdb.users.getUser(userid)
if user is None:
name = 'a removed user'
else:
name = user.name
if int(active):
active = 'Active'
else:
active = 'Inactive'
2003-12-07 06:33:31 +01:00
if pri:
task += ', priority: %s' % pri
2004-01-18 08:58:26 +01:00
added_at = time.strftime(conf.supybot.humanTimestampFormat(),
2003-12-07 06:33:31 +01:00
time.localtime(int(added_at)))
2003-12-08 14:15:00 +01:00
s = "%s todo for %s: %s (Added at %s)" % \
(active, name, task, added_at)
irc.reply(s)
2003-10-21 06:50:03 +02:00
def add(self, irc, msg, args):
"""[--priority=<num>] <text>
Adds <text> as a task in your own personal todo list. The optional
priority argument allows you to set a task as a high or low priority.
Any integer is valid.
"""
try:
id = ircdb.users.getUserId(msg.prefix)
except KeyError:
2004-01-08 22:49:10 +01:00
irc.errorNotRegistered()
return
2004-07-21 21:36:35 +02:00
(optlist, rest) = getopt.getopt(args, '', ['priority='])
priority = 0
for (option, arg) in optlist:
if option == '--priority':
try:
priority = int(arg)
except ValueError, e:
2004-09-17 07:09:14 +02:00
irc.error('%r is an invalid priority' % arg)
return
2003-12-07 06:33:31 +01:00
text = privmsgs.getArgs(rest)
2003-12-08 14:08:14 +01:00
db = self.dbHandler.getDb()
cursor = db.cursor()
now = int(time.time())
cursor.execute("""INSERT INTO todo
VALUES (NULL, %s, %s, %s, %s, 1)""",
priority, now, id, text)
2003-12-08 14:08:14 +01:00
db.commit()
2004-07-21 21:36:35 +02:00
cursor.execute("""SELECT id FROM todo
WHERE added_at=%s AND userid=%s""", now, id)
todoId = cursor.fetchone()[0]
irc.replySuccess('(Todo #%s added)' % (todoId))
2003-10-21 06:50:03 +02:00
def remove(self, irc, msg, args):
"""<task id> [<task id> ...]
Removes <task id> from your personal todo list.
"""
try:
id = ircdb.users.getUserId(msg.prefix)
except KeyError:
2004-01-08 22:49:10 +01:00
irc.errorNotRegistered()
return
taskids = privmsgs.getArgs(args)
tasks = taskids.split()
#print 'Tasks: %s' % repr(tasks)
2003-12-08 14:08:14 +01:00
db = self.dbHandler.getDb()
cursor = db.cursor()
invalid = []
for taskid in tasks:
cursor.execute("""SELECT * FROM todo
WHERE id = %s AND userid = %s
AND active = 1""", taskid, id)
#print 'Rowcount: %s' % cursor.rowcount
if cursor.rowcount == 0:
invalid.append(taskid)
#print 'Invalid tasks: %s' % repr(invalid)
if invalid and len(invalid) == 1:
irc.error('Task %s could not be removed either because '
'that id doesn\'t exist, the todo doesn\'t '
'belong to you, or it has been removed '
'already.' % invalid[0])
elif invalid:
irc.error('No tasks were removed because the following '
'tasks could not be removed: %s' %
utils.commaAndify(invalid))
else:
for taskid in tasks:
cursor.execute("""UPDATE todo SET active = 0 WHERE id = %s""",
taskid)
db.commit()
irc.replySuccess()
_sqlTrans = string.maketrans('*?', '%_')
2003-10-21 06:50:03 +02:00
def search(self, irc, msg, args):
"""[--{regexp}=<value>] [<glob>]
Searches the todos for tasks matching <glob>. If --regexp is given,
2003-12-10 23:55:48 +01:00
its associated value is taken as a regexp and matched against the
tasks.
"""
try:
id = ircdb.users.getUserId(msg.prefix)
except KeyError:
2004-01-08 22:49:10 +01:00
irc.errorNotRegistered()
return
(optlist, rest) = getopt.getopt(args, '', ['regexp='])
if not optlist and not rest:
raise callbacks.ArgumentError
2003-12-08 14:08:14 +01:00
db = self.dbHandler.getDb()
2003-12-07 06:33:31 +01:00
criteria = ['userid=%s' % id, 'active=1']
formats = []
predicateName = 'p'
for (option, arg) in optlist:
if option == '--regexp':
criteria.append('%s(task)' % predicateName)
try:
r = utils.perlReToPythonRe(arg)
except ValueError, e:
2004-09-17 07:09:14 +02:00
irc.error('%r is not a valid regular expression' %
arg)
return
def p(s, r=r):
return int(bool(r.search(s)))
2003-12-08 14:08:14 +01:00
db.create_function(predicateName, 1, p)
predicateName += 'p'
for glob in rest:
criteria.append('task LIKE %s')
2003-12-11 01:27:01 +01:00
if '?' not in glob and '*' not in glob:
glob = '*%s*' % glob
formats.append(glob.translate(self._sqlTrans))
2003-12-08 14:08:14 +01:00
cursor = db.cursor()
sql = """SELECT id, task FROM todo WHERE %s""" % ' AND '.join(criteria)
cursor.execute(sql, formats)
if cursor.rowcount == 0:
irc.reply('No tasks matched that query.')
else:
2003-12-07 06:33:31 +01:00
tasks = ['#%s: %s' % (item[0], self._shrink(item[1]))
for item in cursor.fetchall()]
irc.reply(utils.commaAndify(tasks))
2003-10-24 09:12:51 +02:00
def setpriority(self, irc, msg, args):
"""<id> <priority>
Sets the priority of the todo with the given id to the specified value.
"""
try:
user_id = ircdb.users.getUserId(msg.prefix)
except KeyError:
2004-01-08 22:49:10 +01:00
irc.errorNotRegistered()
2003-10-24 09:12:51 +02:00
return
(id, priority) = privmsgs.getArgs(args, required=2)
2003-12-08 14:08:14 +01:00
db = self.dbHandler.getDb()
cursor = db.cursor()
2003-10-24 09:12:51 +02:00
cursor.execute("""SELECT userid, priority FROM todo
WHERE id = %s AND active = 1""", id)
2003-10-24 09:12:51 +02:00
if cursor.rowcount == 0:
irc.error('No note with id %s' % id)
2003-10-24 09:12:51 +02:00
return
(userid, oldpriority) = cursor.fetchone()
if userid != user_id:
irc.error('Todo #%s does not belong to you.' % id)
2003-10-24 09:12:51 +02:00
return
# If we make it here, we're okay
cursor.execute("""UPDATE todo SET priority = %s
WHERE id = %s""", priority, id)
2003-12-08 14:08:14 +01:00
db.commit()
irc.replySuccess()
2003-10-24 09:12:51 +02:00
2003-10-28 19:43:43 +01:00
def change(self, irc, msg, args):
"""<task id> <regexp>
Modify the task with the given id using the supplied regexp.
"""
try:
userid = ircdb.users.getUserId(msg.prefix)
except KeyError:
2004-01-08 22:49:10 +01:00
irc.errorNotRegistered()
2003-10-28 19:43:43 +01:00
return
taskid, regexp = privmsgs.getArgs(args, required=2)
2003-10-28 19:43:43 +01:00
# Check the regexp first, it's easier and doesn't require a db query
try:
replacer = utils.perlReToReplacer(regexp)
except ValueError:
2004-09-17 07:09:14 +02:00
irc.error('%r is not a valid regexp' % regexp)
return
2003-12-08 14:08:14 +01:00
db = self.dbHandler.getDb()
cursor = db.cursor()
2003-10-28 19:43:43 +01:00
cursor.execute("""SELECT task FROM todo
WHERE userid = %s AND id = %s
AND active = 1""", userid, taskid)
if cursor.rowcount == 0:
2004-09-17 07:09:14 +02:00
irc.error('%r is not a valid task id' % taskid)
return
2003-10-28 19:43:43 +01:00
newtext = replacer(cursor.fetchone()[0])
2004-09-17 07:09:14 +02:00
cursor.execute("""UPDATE todo SET task = %s
WHERE id = %s""", newtext, taskid)
2003-12-08 14:08:14 +01:00
db.commit()
irc.replySuccess()
2003-10-28 19:43:43 +01:00
Class = Todo
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: