Aka: Add SQLite3 backend.

This commit is contained in:
Valentin Lorentz 2014-01-22 15:16:12 +01:00
parent 1c617ea742
commit 5aa0c8cfbd
2 changed files with 170 additions and 30 deletions

View File

@ -12,11 +12,8 @@ isn't any way to get around it. You can get it from [Python homepage].
[Twisted] -- Version 1.2.0 or greater (unless you actually want to use Twisted, [Twisted] -- Version 1.2.0 or greater (unless you actually want to use Twisted,
you don't actually need it) you don't actually need it)
[SQLAlchemy] -- If you want the Aka plugin (and probably other plugins in the future)
[PySQLite]:https://code.google.com/p/pysqlite/ [PySQLite]:https://code.google.com/p/pysqlite/
[Twisted]:http://twistedmatrix.com/trac/ [Twisted]:http://twistedmatrix.com/trac/
[SQLAlchemy]:http://www.sqlalchemy.org/
For more information and help on how to use Supybot, checkout For more information and help on how to use Supybot, checkout
the documents under [docs/], especially [GETTING_STARTED] and the documents under [docs/], especially [GETTING_STARTED] and

View File

@ -32,6 +32,7 @@ import re
import os import os
import sys import sys
import datetime import datetime
import operator
import supybot.conf as conf import supybot.conf as conf
import supybot.utils as utils import supybot.utils as utils
@ -43,18 +44,166 @@ import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization from supybot.i18n import PluginInternationalization
_ = PluginInternationalization('Aka') _ = PluginInternationalization('Aka')
try:
import sqlite3
except ImportError:
sqlite3 = None
try: try:
import sqlalchemy import sqlalchemy
import sqlalchemy.ext import sqlalchemy.ext
import sqlalchemy.ext.declarative import sqlalchemy.ext.declarative
except ImportError: except ImportError:
raise callbacks.Error('You have to install python-sqlalchemy in order ' sqlalchemy = None
'to load this plugin.')
if sqlalchemy: if not (sqlite3 or sqlalchemy):
raise callbacks.Error('You have to install python-sqlite3 or '
'python-sqlalchemy in order to load this plugin.')
available_db = {}
class Alias(object):
__slots__ = ('name', 'alias', 'locked', 'locked_by', 'locked_at')
def __init__(self, name, alias):
self.name = name
self.alias = alias
self.locked = False
self.locked_by = None
self.locked_at = None
def __repr__(self):
return "<Alias('%r', '%r')>" % (self.name, self.alias)
if sqlite3:
class SQLiteAlias(Alias):
__slots__ = ()
pass
class SQLiteAkaDB(object):
__slots__ = ('engines', 'filename', 'dbs',)
def __init__(self, filename):
self.engines = ircutils.IrcDict()
self.filename = filename.replace('sqlite3', 'sqlalchemy')
def close(self):
self.dbs.clear()
def get_db(self, channel):
if channel in self.engines:
engine = self.engines[channel]
else:
filename = plugins.makeChannelFilename(self.filename, channel)
exists = os.path.exists(filename)
engine = sqlite3.connect(filename, check_same_thread=False)
if not exists:
cursor = engine.cursor()
cursor.execute("""CREATE TABLE aliases (
id INTEGER NOT NULL,
name VARCHAR NOT NULL,
alias VARCHAR NOT NULL,
locked BOOLEAN NOT NULL,
locked_by VARCHAR,
locked_at DATETIME,
PRIMARY KEY (id),
UNIQUE (name))""")
engine.commit()
self.engines[channel] = engine
assert engine.execute("select 1").fetchone() == (1,)
return engine
def has_aka(self, channel, name):
name = callbacks.canonicalName(name, preserve_spaces=True)
if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8')
db = self.get_db(channel)
return self.get_db(channel).cursor() \
.execute("""SELECT COUNT() as count
FROM aliases WHERE name = ?;""", (name,)) \
.fetchone()[0]
def get_aka_list(self, channel):
cursor = self.get_db(channel).cursor()
cursor.execute("""SELECT name FROM aliases;""")
list_ = cursor.fetchall()
return list_
def get_alias(self, channel, name):
name = callbacks.canonicalName(name, preserve_spaces=True)
if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8')
cursor = self.get_db(channel).cursor()
cursor.execute("""SELECT alias FROM aliases
WHERE name = ?;""", (name,))
r = cursor.fetchone()
if r:
return r[0]
else:
return None
def add_aka(self, channel, name, alias):
name = callbacks.canonicalName(name, preserve_spaces=True)
if self.has_aka(channel, name):
raise AkaError(_('This Aka already exists.'))
if sys.version_info[0] < 3:
if isinstance(name, str):
name = name.decode('utf8')
if isinstance(alias, str):
alias = alias.decode('utf8')
db = self.get_db(channel)
cursor = db.cursor().execute('SELECT MAX(id) FROM aliases;')
max_id = cursor.fetchone()[0]
if max_id is None:
max_id = 0
cursor.execute("""INSERT INTO aliases VALUES (
?, ?, ?, 0, NULL, NULL);""", (str(max_id+1), name, alias))
db.commit()
def remove_aka(self, channel, name):
name = callbacks.canonicalName(name, preserve_spaces=True)
if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8')
db = self.get_db(channel)
db.cursor().execute('DELETE FROM aliases WHERE name = ?', (name,))
db.commit()
def lock_aka(self, channel, name, by):
name = callbacks.canonicalName(name, preserve_spaces=True)
if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8')
db = self.get_db(channel)
cursor = db.cursor().execute("""UPDATE aliases
SET locked=1, locked_at=?, locked_by=? WHERE name = ?""",
(datetime.datetime.now(), by, name))
if cursor.rowcount == 0:
raise AkaError(_('This Aka does not exist'))
db.commit()
def unlock_aka(self, channel, name, by):
name = callbacks.canonicalName(name, preserve_spaces=True)
if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8')
db = self.get_db(channel)
cursor = db.cursor()
cursor.execute("""UPDATE aliases SET locked=0, locked_at=?
WHERE name = ?""", (datetime.datetime.now(), name))
if cursor.rowcount == 0:
raise AkaError(_('This Aka does not exist'))
db.commit()
def get_aka_lock(self, channel, name):
name = callbacks.canonicalName(name, preserve_spaces=True)
if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8')
cursor = self.get_db(channel).cursor()
cursor.execute("""SELECT locked, locked_by, locked_at
FROM aliases WHERE name = ?;""", (name,))
r = cursor.fetchone()
if r:
return (bool(r[0]), r[1], r[2])
else:
raise AkaError(_('This Aka does not exist'))
available_db.update({'sqlite3': SQLiteAkaDB})
elif sqlalchemy:
Base = sqlalchemy.ext.declarative.declarative_base() Base = sqlalchemy.ext.declarative.declarative_base()
class Alias(Base): class SQLAlchemyAlias(Alias, Base):
__slots__ = ()
__tablename__ = 'aliases' __tablename__ = 'aliases'
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
@ -65,18 +214,10 @@ if sqlalchemy:
locked_by = sqlalchemy.Column(sqlalchemy.String, nullable=True) locked_by = sqlalchemy.Column(sqlalchemy.String, nullable=True)
locked_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=True) locked_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=True)
def __init__(self, name, alias):
self.name = name
self.alias = alias
self.locked = False
self.locked_by = None
self.locked_at = None
def __repr__(self):
return "<Alias('%r', '%r')>" % (self.name, self.alias)
# TODO: Add table for usage statistics # TODO: Add table for usage statistics
class SqlAlchemyAkaDB(object): class SqlAlchemyAkaDB(object):
__slots__ = ('engines', 'filename', 'sqlalchemy', 'dbs')
def __init__(self, filename): def __init__(self, filename):
self.engines = ircutils.IrcDict() self.engines = ircutils.IrcDict()
self.filename = filename self.filename = filename
@ -105,12 +246,12 @@ if sqlalchemy:
name = callbacks.canonicalName(name, preserve_spaces=True) name = callbacks.canonicalName(name, preserve_spaces=True)
if sys.version_info[0] < 3 and isinstance(name, str): if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8') name = name.decode('utf8')
count = self.get_db(channel).query(Alias) \ count = self.get_db(channel).query(SQLAlchemyAlias) \
.filter(Alias.name == name) \ .filter(SQLAlchemyAlias.name == name) \
.count() .count()
return bool(count) return bool(count)
def get_aka_list(self, channel): def get_aka_list(self, channel):
list_ = list(self.get_db(channel).query(Alias.name)) list_ = list(self.get_db(channel).query(SQLAlchemyAlias.name))
return list_ return list_
def get_alias(self, channel, name): def get_alias(self, channel, name):
@ -118,8 +259,8 @@ if sqlalchemy:
if sys.version_info[0] < 3 and isinstance(name, str): if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8') name = name.decode('utf8')
try: try:
return self.get_db(channel).query(Alias.alias) \ return self.get_db(channel).query(SQLAlchemyAlias.alias) \
.filter(Alias.name == name).one()[0] .filter(SQLAlchemyAlias.name == name).one()[0]
except sqlalchemy.orm.exc.NoResultFound: except sqlalchemy.orm.exc.NoResultFound:
return None return None
@ -133,7 +274,7 @@ if sqlalchemy:
if isinstance(alias, str): if isinstance(alias, str):
alias = alias.decode('utf8') alias = alias.decode('utf8')
db = self.get_db(channel) db = self.get_db(channel)
db.add(Alias(name, alias)) db.add(SQLAlchemyAlias(name, alias))
db.commit() db.commit()
def remove_aka(self, channel, name): def remove_aka(self, channel, name):
@ -141,7 +282,7 @@ if sqlalchemy:
if sys.version_info[0] < 3 and isinstance(name, str): if sys.version_info[0] < 3 and isinstance(name, str):
name = name.decode('utf8') name = name.decode('utf8')
db = self.get_db(channel) db = self.get_db(channel)
db.query(Alias).filter(Alias.name == name).delete() db.query(SQLAlchemyAlias).filter(SQLAlchemyAlias.name == name).delete()
db.commit() db.commit()
def lock_aka(self, channel, name, by): def lock_aka(self, channel, name, by):
@ -150,8 +291,8 @@ if sqlalchemy:
name = name.decode('utf8') name = name.decode('utf8')
db = self.get_db(channel) db = self.get_db(channel)
try: try:
aka = db.query(Alias) \ aka = db.query(SQLAlchemyAlias) \
.filter(Alias.name == name).one() .filter(SQLAlchemyAlias.name == name).one()
except sqlalchemy.orm.exc.NoResultFound: except sqlalchemy.orm.exc.NoResultFound:
raise AkaError(_('This Aka does not exist')) raise AkaError(_('This Aka does not exist'))
if aka.locked: if aka.locked:
@ -167,8 +308,8 @@ if sqlalchemy:
name = name.decode('utf8') name = name.decode('utf8')
db = self.get_db(channel) db = self.get_db(channel)
try: try:
aka = db.query(Alias) \ aka = db.query(SQLAlchemyAlias) \
.filter(Alias.name == name).one() .filter(SQLAlchemyAlias.name == name).one()
except sqlalchemy.orm.exc.NoResultFound: except sqlalchemy.orm.exc.NoResultFound:
raise AkaError(_('This Aka does not exist')) raise AkaError(_('This Aka does not exist'))
if not aka.locked: if not aka.locked:
@ -184,11 +325,13 @@ if sqlalchemy:
name = name.decode('utf8') name = name.decode('utf8')
try: try:
return self.get_db(channel) \ return self.get_db(channel) \
.query(Alias.locked, Alias.locked_by, Alias.locked_at)\ .query(SQLAlchemyAlias.locked, SQLAlchemyAlias.locked_by, SQLAlchemyAlias.locked_at)\
.filter(Alias.name == name).one() .filter(SQLAlchemyAlias.name == name).one()
except sqlalchemy.orm.exc.NoResultFound: except sqlalchemy.orm.exc.NoResultFound:
raise AkaError(_('This Aka does not exist')) raise AkaError(_('This Aka does not exist'))
available_db.update({'sqlalchemy': SqlAlchemyAkaDB})
def getArgs(args, required=1, optional=0, wildcard=0): def getArgs(args, required=1, optional=0, wildcard=0):
if len(args) < required: if len(args) < required:
@ -229,7 +372,7 @@ def findBiggestAt(alias):
else: else:
return 0 return 0
AkaDB = plugins.DB('Aka', {'sqlalchemy': SqlAlchemyAkaDB}) AkaDB = plugins.DB('Aka', available_db)
class Aka(callbacks.Plugin): class Aka(callbacks.Plugin):
"""Add the help for "@plugin help Aka" here """Add the help for "@plugin help Aka" here