comparison src/memory/sqlite.py @ 669:ffb716804580

core, bridge: extra parameter is saved in history: - added extra data in getHistory return value - extra data is saved in database /!\ WARNING: DATABASE SCHEMA HAS CHANGED /!\ to update sqlite database: ALTER TABLE history ADD COLUMN extra BLOB;
author Goffi <goffi@goffi.org>
date Thu, 07 Nov 2013 15:29:39 +0100
parents 70988f08d0ad
children 66633443bcd7
comparison
equal deleted inserted replaced
668:7bb50096d225 669:ffb716804580
49 "INSERT INTO message_types VALUES ('chat')", 49 "INSERT INTO message_types VALUES ('chat')",
50 "INSERT INTO message_types VALUES ('error')", 50 "INSERT INTO message_types VALUES ('error')",
51 "INSERT INTO message_types VALUES ('groupchat')", 51 "INSERT INTO message_types VALUES ('groupchat')",
52 "INSERT INTO message_types VALUES ('headline')", 52 "INSERT INTO message_types VALUES ('headline')",
53 "INSERT INTO message_types VALUES ('normal')", 53 "INSERT INTO message_types VALUES ('normal')",
54 "CREATE TABLE history (id INTEGER PRIMARY KEY ASC, profile_id INTEGER, source TEXT, dest TEXT, source_res TEXT, dest_res TEXT, timestamp DATETIME, message TEXT, type TEXT, FOREIGN KEY(profile_id) REFERENCES profiles(id) ON DELETE CASCADE, FOREIGN KEY(type) REFERENCES message_types(type))", 54 "CREATE TABLE history (id INTEGER PRIMARY KEY ASC, profile_id INTEGER, source TEXT, dest TEXT, source_res TEXT, dest_res TEXT, timestamp DATETIME, message TEXT, type TEXT, extra BLOB, FOREIGN KEY(profile_id) REFERENCES profiles(id) ON DELETE CASCADE, FOREIGN KEY(type) REFERENCES message_types(type))",
55 "CREATE TABLE param_gen (category TEXT, name TEXT, value TEXT, PRIMARY KEY (category,name))", 55 "CREATE TABLE param_gen (category TEXT, name TEXT, value TEXT, PRIMARY KEY (category,name))",
56 "CREATE TABLE param_ind (category TEXT, name TEXT, profile_id INTEGER, value TEXT, PRIMARY KEY (category,name,profile_id), FOREIGN KEY(profile_id) REFERENCES profiles(id) ON DELETE CASCADE)", 56 "CREATE TABLE param_ind (category TEXT, name TEXT, profile_id INTEGER, value TEXT, PRIMARY KEY (category,name,profile_id), FOREIGN KEY(profile_id) REFERENCES profiles(id) ON DELETE CASCADE)",
57 "CREATE TABLE private_gen (namespace TEXT, key TEXT, value TEXT, PRIMARY KEY (namespace, key))", 57 "CREATE TABLE private_gen (namespace TEXT, key TEXT, value TEXT, PRIMARY KEY (namespace, key))",
58 "CREATE TABLE private_ind (namespace TEXT, key TEXT, profile_id INTEGER, value TEXT, PRIMARY KEY (namespace, key, profile_id), FOREIGN KEY(profile_id) REFERENCES profiles(id) ON DELETE CASCADE)", 58 "CREATE TABLE private_ind (namespace TEXT, key TEXT, profile_id INTEGER, value TEXT, PRIMARY KEY (namespace, key, profile_id), FOREIGN KEY(profile_id) REFERENCES profiles(id) ON DELETE CASCADE)",
59 "CREATE TABLE private_gen_bin (namespace TEXT, key TEXT, value BLOB, PRIMARY KEY (namespace, key))", 59 "CREATE TABLE private_gen_bin (namespace TEXT, key TEXT, value BLOB, PRIMARY KEY (namespace, key))",
175 d = self.dbpool.runQuery("REPLACE INTO param_ind(category,name,profile_id,value) VALUES (?,?,?,?)", (category, name, self.profiles[profile], value)) 175 d = self.dbpool.runQuery("REPLACE INTO param_ind(category,name,profile_id,value) VALUES (?,?,?,?)", (category, name, self.profiles[profile], value))
176 d.addErrback(lambda ignore: error(_("Can't set individual parameter (%(category)s/%(name)s) for [%(profile)s] in database" % {"category": category, "name": name, "profile": profile}))) 176 d.addErrback(lambda ignore: error(_("Can't set individual parameter (%(category)s/%(name)s) for [%(profile)s] in database" % {"category": category, "name": name, "profile": profile})))
177 return d 177 return d
178 178
179 #History 179 #History
180 def addToHistory(self, from_jid, to_jid, message, _type='chat', timestamp=None, profile=None): 180 def addToHistory(self, from_jid, to_jid, message, _type='chat', extra=None, timestamp=None, profile=None):
181 """Store a new message in history 181 """Store a new message in history
182 @param from_jid: full source JID 182 @param from_jid: full source JID
183 @param to_jid: full dest JID 183 @param to_jid: full dest JID
184 @param message: message 184 @param message: message
185 @param _type: message type (see RFC 6121 §5.2.2) 185 @param _type: message type (see RFC 6121 §5.2.2)
186 @param extra: dictionary (keys and values are unicode) of extra message data
186 @param timestamp: timestamp in seconds since epoch, or None to use current time 187 @param timestamp: timestamp in seconds since epoch, or None to use current time
187 """ 188 """
188 assert(profile) 189 assert(profile)
189 d = self.dbpool.runQuery("INSERT INTO history(source, source_res, dest, dest_res, timestamp, message, type, profile_id) VALUES (?,?,?,?,?,?,?,?)", 190 if extra is None:
191 extra = {}
192 d = self.dbpool.runQuery("INSERT INTO history(source, source_res, dest, dest_res, timestamp, message, type, extra, profile_id) VALUES (?,?,?,?,?,?,?,?,?)",
190 (from_jid.userhost(), from_jid.resource, to_jid.userhost(), to_jid.resource, timestamp or time(), 193 (from_jid.userhost(), from_jid.resource, to_jid.userhost(), to_jid.resource, timestamp or time(),
191 message, _type, self.profiles[profile])) 194 message, _type, pickle.dumps(extra, 0), self.profiles[profile]))
192 d.addErrback(lambda ignore: error(_("Can't save following message in history: from [%(from_jid)s] to [%(to_jid)s] ==> [%(message)s]" % 195 d.addErrback(lambda ignore: error(_("Can't save following message in history: from [%(from_jid)s] to [%(to_jid)s] ==> [%(message)s]" %
193 {"from_jid": from_jid.full(), "to_jid": to_jid.full(), "message": message}))) 196 {"from_jid": from_jid.full(), "to_jid": to_jid.full(), "message": message})))
194 return d 197 return d
195 198
196 def getHistory(self, from_jid, to_jid, limit=0, between=True, profile=None): 199 def getHistory(self, from_jid, to_jid, limit=0, between=True, profile=None):
199 @param to_jid: dest JID (full, or bare for catchall 202 @param to_jid: dest JID (full, or bare for catchall
200 @param size: maximum number of messages to get, or 0 for unlimited 203 @param size: maximum number of messages to get, or 0 for unlimited
201 """ 204 """
202 assert(profile) 205 assert(profile)
203 206
204 def sqliteToDict(query_result): 207 def sqliteToList(query_result):
205 query_result.reverse() 208 query_result.reverse()
206 result = [] 209 result = []
207 for row in query_result: 210 for row in query_result:
208 timestamp, source, source_res, dest, dest_res, message, _type = row 211 timestamp, source, source_res, dest, dest_res, message, type_, extra_raw = row
212 try:
213 extra = pickle.loads(str(extra_raw or ""))
214 except EOFError:
215 extra = {}
209 result.append((timestamp, "%s/%s" % (source, source_res) if source_res else source, 216 result.append((timestamp, "%s/%s" % (source, source_res) if source_res else source,
210 "%s/%s" % (dest, dest_res) if dest_res else dest, 217 "%s/%s" % (dest, dest_res) if dest_res else dest,
211 message, _type)) 218 message, type_, extra))
212 return result 219 return result
213 220
214 query_parts = ["SELECT timestamp, source, source_res, dest, dest_res, message, type FROM history WHERE profile_id=? AND"] 221 query_parts = ["SELECT timestamp, source, source_res, dest, dest_res, message, type, extra FROM history WHERE profile_id=? AND"]
215 values = [self.profiles[profile]] 222 values = [self.profiles[profile]]
216 223
217 def test_jid(_type, _jid): 224 def test_jid(type_, _jid):
218 values.append(_jid.userhost()) 225 values.append(_jid.userhost())
219 if _jid.resource: 226 if _jid.resource:
220 values.append(_jid.resource) 227 values.append(_jid.resource)
221 return '(%s=? AND %s_res=?)' % (_type, _type) 228 return '(%s=? AND %s_res=?)' % (type_, type_)
222 return '%s=?' % (_type, ) 229 return '%s=?' % (type_, )
223 230
224 if between: 231 if between:
225 query_parts.append("(%s OR %s) AND (%s or %s)" % (test_jid('source', from_jid), 232 query_parts.append("(%s OR %s) AND (%s or %s)" % (test_jid('source', from_jid),
226 test_jid('source', to_jid), 233 test_jid('source', to_jid),
227 test_jid('dest', to_jid), 234 test_jid('dest', to_jid),
235 if limit: 242 if limit:
236 query_parts.append("LIMIT ?") 243 query_parts.append("LIMIT ?")
237 values.append(limit) 244 values.append(limit)
238 245
239 d = self.dbpool.runQuery(" ".join(query_parts), values) 246 d = self.dbpool.runQuery(" ".join(query_parts), values)
240 return d.addCallback(sqliteToDict) 247 return d.addCallback(sqliteToList)
241 248
242 #Private values 249 #Private values
243 def loadGenPrivates(self, private_gen, namespace): 250 def loadGenPrivates(self, private_gen, namespace):
244 """Load general private values 251 """Load general private values
245 @param private_gen: dictionary to fill 252 @param private_gen: dictionary to fill