diff --git a/pyircbot/modules/AttributeStorageLite.py b/pyircbot/modules/AttributeStorageLite.py new file mode 100644 index 0000000..4ef3275 --- /dev/null +++ b/pyircbot/modules/AttributeStorageLite.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +""" +.. module:: AttributeStorageLite + :synopsis: An item key->value storage engine based on Sqlite + +.. moduleauthor:: Dave Pedu + +""" + +from modulebase import ModuleBase,ModuleHook + +class AttributeStorageLite(ModuleBase): + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.services=["attributes"] + self.db = None + serviceProviders = self.bot.getmodulesbyservice("sqlite") + if len(serviceProviders)==0: + self.log.error("AttributeStorage: Could not find a valid sqlite service provider") + else: + self.log.info("AttributeStorage: Selecting sqlite service provider: %s" % serviceProviders[0]) + self.db = serviceProviders[0].opendb("attributes.db") + + if not self.db.tableExists("attribute"): + self.log.info("AttributeStorage: Creating table: attribute") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `attribute` ( + `id` INTEGER PRIMARY KEY, + `attribute` varchar(128) UNIQUE + ) ;""") + c.close() + + if not self.db.tableExists("items"): + self.log.info("AttributeStorage: Creating table: items") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `items` ( + `id` INTEGER PRIMARY KEY, + `item` varchar(512) + ) ;""") + c.close() + + if not self.db.tableExists("values"): + self.log.info("AttributeStorage: Creating table: values") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `values` ( + `itemid` INTEGER NOT NULL, + `attributeid` INTEGER NOT NULL, + `value` TEXT, + PRIMARY KEY (`itemid`, `attributeid`) + ) ;""") + c.close() + + #print(self.setKey('xmopxshell', 'name', 'dave')) + #print(self.getKey('xmopxshell', 'name')) + #print(self.getItem('xMopxShell')) + + def getItem(self, name): + """Get all values for a item + + :param name: the item + :type name: str + :returns: dict -- the item's values expressed as a dict""" + c = self.db.query("""SELECT + `i`.`id`, + `i`.`item`, + `a`.`attribute`, + `v`.`value` + FROM + `items` `i` + INNER JOIN `values` `v` + on `v`.`itemid`=`i`.`id` + INNER JOIN `attribute` `a` + on `a`.`id`=`v`.`attributeid` + + WHERE + `i`.`item`=?;""", + (name.lower(),) + ) + item = {} + while True: + row = c.fetchone() + if row == None: + break + item[row["attribute"]]=row["value"] + c.close() + + if len(item)==0: + return {} + return item + + def getKey(self, item, key): + """Get the value of an key on an item + + :param item: the item to fetch a key from + :type item: str + :param key: they key who's value to return + :type key: str + :returns: str -- the item from the database or **None**""" + c = self.db.query("""SELECT + `i`.`id`, + `i`.`item`, + `a`.`attribute`, + `v`.`value` + FROM + `items` `i` + INNER JOIN `values` `v` + on `v`.`itemid`=`i`.`id` + INNER JOIN `attribute` `a` + on `a`.`id`=`v`.`attributeid` + + WHERE + `i`.`item`=? + AND + `a`.`attribute`=?;""", + (item.lower(),key.lower()) + ) + row = c.fetchone() + + c.close() + if row == None: + return None + return row["value"] + + def setKey(self, item, key, value): + """Set the key on an item + + :param item: the item name to set the key on + :type item: str + :param key: the key to set + :type key: tuple + :param value: the value to set + :type value: str""" + item = item.lower() + attribute = key.lower() + + # Check attribute exists + c = self.db.query("SELECT `id` FROM `attribute` WHERE `attribute`=?;", (attribute,)) + row = c.fetchone() + attributeId = -1 + if row == None: + c = self.db.query("INSERT INTO `attribute` (`attribute`) VALUES (?);", (attribute,)) + attributeId = c.lastrowid + else: + attributeId = row["id"] + c.close() + + # check item exists + c = self.db.query("SELECT `id` FROM `items` WHERE `item`=?;", (item,)) + row = c.fetchone() + itemId = -1 + if row == None: + c = self.db.query("INSERT INTO `items` (`item`) VALUES (?);", (item,)) + itemId = c.lastrowid + else: + itemId = row["id"] + c.close() + + if value == None: + # delete it + c = self.db.query("DELETE FROM `values` WHERE `itemid`=? AND `attributeid`=? ;", (itemId, attributeId)) + self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value)) + else: + # add attribute + c = self.db.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (?, ?, ?);", (itemId, attributeId, value)) + self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value)) + c.close() diff --git a/pyircbot/modules/SQLite.py b/pyircbot/modules/SQLite.py new file mode 100644 index 0000000..9ee5c13 --- /dev/null +++ b/pyircbot/modules/SQLite.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +""" +.. module:: SQLite + :synopsis: Module providing a sqlite type service + +.. moduleauthor:: Dave Pedu + +""" + +from modulebase import ModuleBase +import sys +import sqlite3 + +class SQLite(ModuleBase): + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.services=["sqlite"] + self.loadConfig() + + def opendb(self, dbname): + return Connection(self, dbname) + + +class Connection: + def __init__(self, master, dbname): + self.master = master + self.log = master.log + self.dbname = dbname + self.connection = None + self._connect() + + # Check if the table requested exists + def tableExists(self, tablename): + c = self.getCursor() + c.execute("SELECT * FROM SQLITE_MASTER WHERE `type`='table' AND `name`=?", (tablename,)) + tables = c.fetchall() + if len(tables)==0: + return False; + return True + + @staticmethod + def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d + + def query(self, queryText, args=()): + """Execute a Sqlite query and return the cursor + + :param queryText: the sqlite query as a string, using '%s' for token replacement + :type queryText: str + :param args: arguments to be escaped into the query + :type args: tuple + :returns: cursor -- the sql cursor""" + c = self.getCursor() + if len(args)==0: + c.execute(queryText) + else: + c.execute(queryText, args) + return c + + # Returns a cusor object, after checking for connectivity + def getCursor(self): + c=self.connection.cursor() + return c + + def escape(self, s): + raise NotImplementedError + + def ondisable(self): + self.connection.close() + + # Connects to the database server, and selects a database (Or attempts to create it if it doesn't exist yet) + def _connect(self): + self.log.info("Sqlite: opening database %s" % self.master.getFilePath(self.dbname)) + self.connection = sqlite3.connect(self.master.getFilePath(self.dbname)) + self.connection.row_factory = Connection.dict_factory + self.connection.isolation_level = None + self.log.info("Sqlite: Connected.") + + # Test the connection + c = self.connection.cursor() + derp=c.execute("SELECT * FROM SQLITE_MASTER") + c.close()