Remove unused option
[cascardo/ipsilon.git] / ipsilon / util / data.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2013  Simo Sorce <simo@redhat.com>
4 #
5 # see file 'COPYING' for use and warranty information
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 import cherrypy
21 from ipsilon.util.log import Log
22 from sqlalchemy import create_engine
23 from sqlalchemy import MetaData, Table, Column, Text
24 from sqlalchemy.sql import select
25 import uuid
26
27
28 OPTIONS_COLUMNS = ['name', 'option', 'value']
29 UNIQUE_DATA_COLUMNS = ['uuid', 'name', 'value']
30
31
32 class SqlStore(Log):
33
34     def __init__(self, name):
35         if name not in cherrypy.config:
36             raise NameError('Unknown database %s' % name)
37         engine_name = cherrypy.config[name]
38         if '://' not in engine_name:
39             engine_name = 'sqlite:///' + engine_name
40         self._dbengine = create_engine(engine_name)
41
42     def engine(self):
43         return self._dbengine
44
45     def connection(self):
46         return self._dbengine.connect()
47
48
49 def SqlAutotable(f):
50     def at(self, *args, **kwargs):
51         self.create()
52         return f(self, *args, **kwargs)
53     return at
54
55
56 class SqlQuery(Log):
57
58     def __init__(self, db_obj, table, columns, trans=True):
59         self._db = db_obj
60         self._con = self._db.connection()
61         self._trans = self._con.begin() if trans else None
62         self._table = self._get_table(table, columns)
63
64     def _get_table(self, name, columns):
65         table = Table(name, MetaData(self._db.engine()))
66         for c in columns:
67             table.append_column(Column(c, Text()))
68         return table
69
70     def _where(self, kvfilter):
71         where = None
72         if kvfilter is not None:
73             for k in kvfilter:
74                 w = self._table.columns[k] == kvfilter[k]
75                 if where is None:
76                     where = w
77                 else:
78                     where = where & w
79         return where
80
81     def _columns(self, columns=None):
82         cols = None
83         if columns is not None:
84             cols = []
85             for c in columns:
86                 cols.append(self._table.columns[c])
87         else:
88             cols = self._table.columns
89         return cols
90
91     def rollback(self):
92         self._trans.rollback()
93
94     def commit(self):
95         self._trans.commit()
96
97     def create(self):
98         self._table.create(checkfirst=True)
99
100     def drop(self):
101         self._table.drop(checkfirst=True)
102
103     @SqlAutotable
104     def select(self, kvfilter=None, columns=None):
105         return self._con.execute(select(self._columns(columns),
106                                         self._where(kvfilter)))
107
108     @SqlAutotable
109     def insert(self, values):
110         self._con.execute(self._table.insert(values))
111
112     @SqlAutotable
113     def update(self, values, kvfilter):
114         self._con.execute(self._table.update(self._where(kvfilter), values))
115
116     @SqlAutotable
117     def delete(self, kvfilter):
118         self._con.execute(self._table.delete(self._where(kvfilter)))
119
120
121 class Store(Log):
122
123     def __init__(self, config_name):
124         self._db = SqlStore(config_name)
125         self._query = SqlQuery
126
127     def _row_to_dict_tree(self, data, row):
128         name = row[0]
129         if len(row) > 2:
130             if name not in data:
131                 data[name] = dict()
132             d2 = data[name]
133             self._row_to_dict_tree(d2, row[1:])
134         else:
135             value = row[1]
136             if name in data:
137                 if data[name] is list:
138                     data[name].append(value)
139                 else:
140                     v = data[name]
141                     data[name] = [v, value]
142             else:
143                 data[name] = value
144
145     def _rows_to_dict_tree(self, rows):
146         data = dict()
147         for r in rows:
148             self._row_to_dict_tree(data, r)
149         return data
150
151     def _load_data(self, table, columns, kvfilter=None):
152         rows = []
153         try:
154             q = self._query(self._db, table, columns, trans=False)
155             rows = q.select(kvfilter)
156         except Exception, e:  # pylint: disable=broad-except
157             self.error("Failed to load data for table %s: [%s]" % (table, e))
158         return self._rows_to_dict_tree(rows)
159
160     def load_config(self):
161         table = 'config'
162         columns = ['name', 'value']
163         return self._load_data(table, columns)
164
165     def load_options(self, table, name=None):
166         kvfilter = dict()
167         if name:
168             kvfilter['name'] = name
169         options = self._load_data(table, OPTIONS_COLUMNS, kvfilter)
170         if name and name in options:
171             return options[name]
172         return options
173
174     def save_options(self, table, name, options):
175         curvals = dict()
176         q = None
177         try:
178             q = self._query(self._db, table, OPTIONS_COLUMNS)
179             rows = q.select({'name': name}, ['option', 'value'])
180             for row in rows:
181                 curvals[row[0]] = row[1]
182
183             for opt in options:
184                 if opt in curvals:
185                     q.update({'value': options[opt]},
186                              {'name': name, 'option': opt})
187                 else:
188                     q.insert((name, opt, options[opt]))
189
190             q.commit()
191         except Exception, e:  # pylint: disable=broad-except
192             if q:
193                 q.rollback()
194             self.error("Failed to save options: [%s]" % e)
195             raise
196
197     def delete_options(self, table, name, options=None):
198         kvfilter = {'name': name}
199         q = None
200         try:
201             q = self._query(self._db, table, OPTIONS_COLUMNS)
202             if options is None:
203                 q.delete(kvfilter)
204             else:
205                 for opt in options:
206                     kvfilter['option'] = opt
207                     q.delete(kvfilter)
208             q.commit()
209         except Exception, e:  # pylint: disable=broad-except
210             if q:
211                 q.rollback()
212             self.error("Failed to delete from %s: [%s]" % (table, e))
213             raise
214
215     def new_unique_data(self, table, data):
216         newid = str(uuid.uuid4())
217         q = None
218         try:
219             q = self._query(self._db, table, UNIQUE_DATA_COLUMNS)
220             for name in data:
221                 q.insert((newid, name, data[name]))
222             q.commit()
223         except Exception, e:  # pylint: disable=broad-except
224             if q:
225                 q.rollback()
226             self.error("Failed to store %s data: [%s]" % (table, e))
227             raise
228         return newid
229
230     def get_unique_data(self, table, uuidval=None, name=None, value=None):
231         kvfilter = dict()
232         if uuidval:
233             kvfilter['uuid'] = uuidval
234         if name:
235             kvfilter['name'] = name
236         if value:
237             kvfilter['value'] = value
238         return self._load_data(table, UNIQUE_DATA_COLUMNS, kvfilter)
239
240     def save_unique_data(self, table, data):
241         q = None
242         try:
243             q = self._query(self._db, table, UNIQUE_DATA_COLUMNS)
244             for uid in data:
245                 curvals = dict()
246                 rows = q.select({'uuid': uid}, ['name', 'value'])
247                 for r in rows:
248                     curvals[r[0]] = r[1]
249
250                 datum = data[uid]
251                 for name in datum:
252                     if name in curvals:
253                         q.update({'value': datum[name]},
254                                  {'uuid': uid, 'name': name})
255                     else:
256                         q.insert((uid, name, datum[name]))
257
258             q.commit()
259         except Exception, e:  # pylint: disable=broad-except
260             if q:
261                 q.rollback()
262             self.error("Failed to store data in %s: [%s]" % (table, e))
263             raise
264
265     def del_unique_data(self, table, uuidval):
266         kvfilter = {'uuid': uuidval}
267         try:
268             q = self._query(self._db, table, UNIQUE_DATA_COLUMNS, trans=False)
269             q.delete(kvfilter)
270         except Exception, e:  # pylint: disable=broad-except
271             self.error("Failed to delete data from %s: [%s]" % (table, e))
272
273     def _reset_data(self, table):
274         try:
275             q = self._query(self._db, table, UNIQUE_DATA_COLUMNS)
276             q.drop()
277             q.create()
278             q.commit()
279         except Exception, e:  # pylint: disable=broad-except
280             if q:
281                 q.rollback()
282             self.error("Failed to erase all data from %s: [%s]" % (table, e))
283
284
285 class AdminStore(Store):
286
287     def __init__(self):
288         super(AdminStore, self).__init__('admin.config.db')
289
290     def get_data(self, plugin, idval=None, name=None, value=None):
291         return self.get_unique_data(plugin+"_data", idval, name, value)
292
293     def save_data(self, plugin, data):
294         return self.save_unique_data(plugin+"_data", data)
295
296     def new_datum(self, plugin, datum):
297         table = plugin+"_data"
298         return self.new_unique_data(table, datum)
299
300     def del_datum(self, plugin, idval):
301         table = plugin+"_data"
302         return self.del_unique_data(table, idval)
303
304     def wipe_data(self, plugin):
305         table = plugin+"_data"
306         self._reset_data(table)
307
308
309 class UserStore(Store):
310
311     def __init__(self, path=None):
312         super(UserStore, self).__init__('user.prefs.db')
313
314     def save_user_preferences(self, user, options):
315         self.save_options('users', user, options)
316
317     def load_user_preferences(self, user):
318         return self.load_options('users', user)
319
320     def save_plugin_data(self, plugin, user, options):
321         self.save_options(plugin+"_data", user, options)
322
323     def load_plugin_data(self, plugin, user):
324         return self.load_options(plugin+"_data", user)
325
326
327 class TranStore(Store):
328
329     def __init__(self, path=None):
330         super(TranStore, self).__init__('transactions.db')