import os
import sqlite3
import cherrypy
+from ipsilon.util.log import Log
+import uuid
-class Store(object):
+OPTIONS_COLUMNS = ['name', 'option', 'value']
+UNIQUE_DATA_COLUMNS = ['uuid', 'name', 'value']
- def __init__(self, path=None):
- if path is None:
- self._path = os.getcwd()
+
+class Store(Log):
+
+ def __init__(self, name):
+ self._dbname = name
+
+ def _build_where(self, kvfilter, kvout):
+ where = ""
+ sep = "WHERE"
+ for k in kvfilter:
+ mk = "where_%s" % k
+ kvout[mk] = kvfilter[k]
+ where += "%s %s=:%s" % (sep, k, mk)
+ sep = " AND"
+ return where
+
+ def _build_select(self, table, kvfilter=None, kvout=None, columns=None):
+ SELECT = "SELECT %(cols)s FROM %(table)s %(where)s"
+ cols = "*"
+ if columns:
+ cols = ",".join(columns)
+ where = ""
+ if kvfilter is not None:
+ where = self._build_where(kvfilter, kvout)
+ return SELECT % {'table': table, 'cols': cols, 'where': where}
+
+ def _select(self, cursor, table, kvfilter=None, columns=None):
+ kv = dict()
+ select = self._build_select(table, kvfilter, kv, columns)
+ cursor.execute(select, kv)
+ return cursor.fetchall()
+
+ def _create(self, cursor, table, columns):
+ CREATE = "CREATE TABLE IF NOT EXISTS %(table)s(%(cols)s)"
+ cols = ",".join(columns)
+ create = CREATE % {'table': table, 'cols': cols}
+ cursor.execute(create)
+
+ def _update(self, cursor, table, values, kvfilter):
+ UPDATE = "UPDATE %(table)s SET %(setval)s %(where)s"
+ kv = dict()
+
+ setval = ""
+ sep = ""
+ for k in values:
+ mk = "setval_%s" % k
+ kv[mk] = values[k]
+ setval += "%s%s=:%s" % (sep, k, mk)
+ sep = " , "
+
+ where = self._build_where(kvfilter, kv)
+
+ update = UPDATE % {'table': table, 'setval': setval, 'where': where}
+ cursor.execute(update, kv)
+
+ def _insert(self, cursor, table, values):
+ INSERT = "INSERT INTO %(table)s VALUES(%(values)s)"
+ vals = ""
+ sep = ""
+ for _ in values:
+ vals += "%s?" % sep
+ sep = ","
+ insert = INSERT % {'table': table, 'values': vals}
+ cursor.execute(insert, values)
+
+ def _delete(self, cursor, table, kvfilter):
+ DELETE = "DELETE FROM %(table)s %(where)s"
+ kv = dict()
+ where = self._build_where(kvfilter, kv)
+ delete = DELETE % {'table': table, 'where': where}
+ cursor.execute(delete, kv)
+
+ def _row_to_dict_tree(self, data, row):
+ name = row[0]
+ if len(row) > 2:
+ if name not in data:
+ data[name] = dict()
+ d2 = data[name]
+ self._row_to_dict_tree(d2, row[1:])
else:
- self._path = path
+ value = row[1]
+ if name in data:
+ if data[name] is list:
+ data[name].append(value)
+ else:
+ v = data[name]
+ data[name] = [v, value]
+ else:
+ data[name] = value
+
+ def _rows_to_dict_tree(self, rows):
+ data = dict()
+ for r in rows:
+ self._row_to_dict_tree(data, r)
+ return data
- def _load_config(self, dbname):
+ def _load_data(self, table, columns, kvfilter=None):
con = None
rows = []
try:
- con = sqlite3.connect(dbname)
+ con = sqlite3.connect(self._dbname)
cur = con.cursor()
- cur.executescript("""
- CREATE TABLE IF NOT EXISTS config(name TEXT, value TEXT)
- """)
- cur.execute("SELECT * FROM config")
- rows = cur.fetchall()
+ self._create(cur, table, columns)
+ rows = self._select(cur, table, kvfilter)
con.commit()
except sqlite3.Error, e:
if con:
con.rollback()
- cherrypy.log.error("Failed to load config: [%s]" % e)
+ self.error("Failed to load data for table %s: [%s]" % (table, e))
finally:
if con:
con.close()
- conf = {}
- for row in rows:
- if row[0] in conf:
- # multivalued
- if conf[row[0]] is list:
- conf[row[0]].append(row[1])
+ return self._rows_to_dict_tree(rows)
+
+ def load_config(self):
+ table = 'config'
+ columns = ['name', 'value']
+ return self._load_data(table, columns)
+
+ def load_options(self, table, name=None):
+ kvfilter = dict()
+ if name:
+ kvfilter['name'] = name
+ options = self._load_data(table, OPTIONS_COLUMNS, kvfilter)
+ if name and name in options:
+ return options[name]
+ return options
+
+ def save_options(self, table, name, options):
+ curvals = dict()
+ con = None
+ try:
+ con = sqlite3.connect(self._dbname)
+ cur = con.cursor()
+ self._create(cur, table, OPTIONS_COLUMNS)
+ rows = self._select(cur, table, {'name': name},
+ ['option', 'value'])
+ for row in rows:
+ curvals[row[0]] = row[1]
+
+ for opt in options:
+ if opt in curvals:
+ self._update(cur, table,
+ {'value': options[opt]},
+ {'name': name, 'option': opt})
else:
- v = conf[row[0]]
- conf[row[0]] = [v, row[1]]
+ self._insert(cur, table, (name, opt, options[opt]))
+
+ con.commit()
+ except sqlite3.Error, e:
+ if con:
+ con.rollback()
+ self.error("Failed to store config: [%s]" % e)
+ raise
+ finally:
+ if con:
+ con.close()
+
+ def delete_options(self, table, name, options=None):
+ kvfilter = {'name': name}
+ try:
+ con = sqlite3.connect(self._dbname)
+ cur = con.cursor()
+ self._create(cur, table, OPTIONS_COLUMNS)
+ if options is None:
+ self._delete(cur, table, kvfilter)
else:
- conf[row[0]] = row[1]
+ for opt in options:
+ kvfilter['option'] = opt
+ self._delete(cur, table, kvfilter)
+ con.commit()
+ except sqlite3.Error, e:
+ if con:
+ con.rollback()
+ self.error("Failed to delete from %s: [%s]" % (table, e))
+ raise
+ finally:
+ if con:
+ con.close()
+
+ def new_unique_data(self, table, data):
+ con = None
+ try:
+ con = sqlite3.connect(self._dbname)
+ cur = con.cursor()
+ self._create(cur, table, UNIQUE_DATA_COLUMNS)
+ newid = str(uuid.uuid4())
+ for name in data:
+ self._insert(cur, table, (newid, name, data[name]))
+ con.commit()
+ except sqlite3.Error, e:
+ if con:
+ con.rollback()
+ cherrypy.log.error("Failed to store %s data: [%s]" % (table, e))
+ raise
+ finally:
+ if con:
+ con.close()
+ return newid
- return conf
+ def get_unique_data(self, table, uuidval=None, name=None, value=None):
+ kvfilter = dict()
+ if uuidval:
+ kvfilter['uuid'] = uuidval
+ if name:
+ kvfilter['name'] = name
+ if value:
+ kvfilter['value'] = value
+ return self._load_data(table, UNIQUE_DATA_COLUMNS, kvfilter)
- def get_admin_config(self):
- path = None
- if 'admin.config.db' in cherrypy.config:
- path = cherrypy.config['admin.config.db']
- if not path:
- path = os.path.join(self._path, 'adminconfig.sqlite')
+ def save_unique_data(self, table, data):
+ curvals = dict()
+ con = None
+ try:
+ con = sqlite3.connect(self._dbname)
+ cur = con.cursor()
+ self._create(cur, table, UNIQUE_DATA_COLUMNS)
+ for uid in data:
+ curvals = dict()
+ rows = self._select(cur, table, {'uuid': uid},
+ ['name', 'value'])
+ for r in rows:
+ curvals[r[0]] = r[1]
+
+ datum = data[uid]
+ for name in datum:
+ if name in curvals:
+ self._update(cur, table,
+ {'value': datum[name]},
+ {'uuid': uid, 'name': name})
+ else:
+ self._insert(cur, table, (uid, name, datum[name]))
- return self._load_config(path)
+ con.commit()
+ except sqlite3.Error, e:
+ if con:
+ con.rollback()
+ self.error("Failed to store data in %s: [%s]" % (table, e))
+ raise
+ finally:
+ if con:
+ con.close()
- def _load_user_prefs(self, dbname, user):
+ def del_unique_data(self, table, uuidval):
+ kvfilter = {'uuid': uuidval}
con = None
- rows = []
try:
- con = sqlite3.connect(dbname)
+ con = sqlite3.connect(self._dbname)
cur = con.cursor()
- cur.executescript("""
- CREATE TABLE IF NOT EXISTS users(name TEXT,
- option TEXT,
- value TEXT)
- """)
- cur.execute("SELECT option, value FROM users "
- "where name = '%s'" % user)
- rows = cur.fetchall()
+ self._delete(cur, table, kvfilter)
+ except sqlite3.Error, e:
+ self.error("Failed to delete data from %s: [%s]" % (table, e))
+ finally:
+ if con:
+ con.close()
+
+
+class AdminStore(Store):
+
+ def __init__(self, path=None):
+ if path is None:
+ self._path = os.getcwd()
+ else:
+ self._path = path
+ self._name = None
+ if 'admin.config.db' in cherrypy.config:
+ self._name = cherrypy.config['admin.config.db']
+ if not self._name:
+ self._name = os.path.join(self._path, 'adminconfig.sqlite')
+ super(AdminStore, self).__init__(self._name)
+
+ def get_data(self, plugin, idval=None, name=None, value=None):
+ return self.get_unique_data(plugin+"_data", idval, name, value)
+
+ def save_data(self, plugin, data):
+ return self.save_unique_data(plugin+"_data", data)
+
+ def new_datum(self, plugin, datum):
+ table = plugin+"_data"
+ return self.new_unique_data(table, datum)
+
+ def del_datum(self, plugin, idval):
+ table = plugin+"_data"
+ return self.del_unique_data(table, idval)
+
+ def wipe_data(self, plugin):
+ table = plugin+"_data"
+ # Try to backup old data first, just in case
+ try:
+ con = sqlite3.connect(self._dbname)
+ cur = con.cursor()
+ cur.execute("DROP TABLE IF EXISTS " + table)
+ self._create(cur, table, UNIQUE_DATA_COLUMNS)
con.commit()
except sqlite3.Error, e:
if con:
con.rollback()
- cherrypy.log.error("Failed to load %s's prefs from "
- "%s: [%s]" % (user, dbname, e))
+ cherrypy.log.error("Failed to wipe %s data: [%s]" % (plugin, e))
+ raise
finally:
if con:
con.close()
- conf = {}
- for row in rows:
- conf[row[0]] = row[1]
- return conf
+class UserStore(Store):
- def get_user_preferences(self, user):
- path = None
+ def __init__(self, path=None):
+ if path is None:
+ self._path = os.getcwd()
+ else:
+ self._path = path
+ self._name = None
if 'user.prefs.db' in cherrypy.config:
- path = cherrypy.config['user.prefs.db']
- if not path:
- path = os.path.join(self._path, 'userprefs.sqlite')
+ self._name = cherrypy.config['user.prefs.db']
+ if not self._name:
+ self._name = os.path.join(self._path, 'userprefs.sqlite')
+ super(UserStore, self).__init__(self._name)
- return self._load_user_prefs(path, user)
+ def save_user_preferences(self, user, options):
+ return self.save_options('users', user, options)