summaryrefslogtreecommitdiff
path: root/kronos/storage.py
diff options
context:
space:
mode:
Diffstat (limited to 'kronos/storage.py')
-rw-r--r--kronos/storage.py177
1 files changed, 177 insertions, 0 deletions
diff --git a/kronos/storage.py b/kronos/storage.py
new file mode 100644
index 0000000..4add988
--- /dev/null
+++ b/kronos/storage.py
@@ -0,0 +1,177 @@
1# vim: set filencoding=utf8
2"""
3Storage Back-ends For Kronos
4
5@author: Mike Crute (mcrute@ag.com)
6@organization: American Greetings Interactive
7@date: February 04, 2010
8"""
9
10
11class StorageError(Exception):
12 """
13 Base class for all other storage exceptions.
14 """
15
16
17class NotConnected(StorageError):
18 """
19 Raised when a connection to the back-end is not avaiable.
20 """
21
22
23def _get_filtered_object_dict(dict_):
24 output = {}
25 for key, value in dict_.__dict__.items():
26 if not key.startswith('_') and not callable(value):
27 output[key] = value
28
29 return output
30
31
32class BaseStorageBackEnd(object):
33
34 def __init__(self):
35 self.load_engine()
36
37 def get(self, model_obj, **kwargs):
38 """
39 Get a model object instance from the storage back-end
40 where the criteria in the kwargs matches that in the
41 back-end.
42 """
43 name = model_obj.__name__
44 result = self.select(name, **kwargs)
45
46 instance = model_obj()
47 for key, value in result.items():
48 if key != 'id':
49 setattr(instance, key, value)
50 else:
51 instance.__db_id__ = value
52
53 return instance
54
55 def save(self, model_obj):
56 """
57 Persist a model object with the storage back-end.
58 """
59 name = model_obj.__class__.__name__
60 id_ = getattr(model_obj, '__db_id__', None)
61
62 values = _get_filtered_object_dict(model_obj)
63
64 if id_:
65 self.update(name, id_, **values)
66 else:
67 self.insert(name, **values)
68
69 def load_engine(self):
70 """
71 Load a storage engine module that contains a function
72 called connect suitable for setting up the actual
73 connection.
74 """
75
76 def select(self, table, **kwargs):
77 """
78 Select raw data from the storage engine and return
79 as a dictionary.
80 """
81
82 def insert(self, table, **values):
83 """
84 Insert values into the back-end named table or
85 equivalent data structure.
86 """
87
88 def update(self, table, criteria, **values):
89 """
90 Update values in the back-end table or equivalent
91 data structure based on the criteria.
92 """
93
94 def connect(self, database, *args, **kwargs):
95 """
96 Create a connection to the back-end.
97 """
98
99 def create_table(self, obj, **columns):
100 """
101 Create a table or equivalent data structure in the
102 storage back-end.
103 """
104
105
106class SQLiteBackEnd(BaseStorageBackEnd):
107
108 def __init__(self):
109 super(SQLiteBackEnd, self).__init__()
110 self.connection = None
111
112 def load_engine(self):
113 import sqlite3
114 self.engine = sqlite3
115
116 def _check_connection(self):
117 if not self.connection:
118 raise NotConnected("Not connected to storage back-end!")
119
120 def connect(self, database, *args, **kwargs):
121 self.connection = self.engine.connect(database)
122
123 def select(self, table, **kwargs):
124 sql = "SELECT * FROM {0} WHERE ".format(table)
125 for key in kwargs.keys():
126 sql += "{0}=? ".format(key)
127
128 return self._get_normalized_results(sql, **kwargs)[0]
129
130 def _get_normalized_results(self, sql, **kwargs):
131 self._check_connection()
132 cursor = self.connection.cursor()
133
134 results = cursor.execute(sql, kwargs.values())
135 return self._normalize_results(results)
136
137 def _normalize_results(self, results):
138 col_names = [col[0] for col in results.description]
139 output = []
140 for row in results.fetchall():
141 output.append(dict(zip(col_names, row)))
142
143 return output
144
145 def insert(self, table, **values):
146 placeholder = ("?," * len(values)).rstrip(',')
147 col_spec = ','.join(values.keys())
148 sql = "INSERT INTO {0}({1}) VALUES({2})".format(table, col_spec,
149 placeholder)
150
151 self._execute_modification(sql, values.values())
152
153 def update(self, table, criteria, **values):
154 sql = "UPDATE {0} SET ".format(table)
155 for key in values.keys():
156 sql += "{0}=?, ".format(key)
157 sql = sql[:-2] + "WHERE id=?"
158
159 binds = values.values() + [criteria]
160 self._execute_modification(sql, binds)
161
162 def create_table(self, obj, **columns):
163 table = obj.__name__
164
165 sql = "CREATE TABLE {0} (".format(table)
166 sql += "id INTEGER PRIMARY KEY"
167 for key, value in columns.items():
168 sql += ", {0} {1}".format(key, value)
169 sql += ")"
170
171 self._execute_modification(sql)
172
173 def _execute_modification(self, sql, binds=()):
174 self._check_connection()
175 cursor = self.connection.cursor()
176 cursor.execute(sql, binds)
177 self.connection.commit()