Mercurial > libervia-backend
annotate src/tools/sqlite.py @ 427:bb0236633da5
core: outgoing groupchat message are not added to history anymore
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 11 Nov 2011 18:40:23 +0100 |
parents | e4e9187e3b5b |
children |
rev | line source |
---|---|
412 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 SAT: a jabber client | |
6 Copyright (C) 2009, 2010, 2011 Jérôme Poisson (goffi@goffi.org) | |
7 | |
8 This program is free software: you can redistribute it and/or modify | |
9 it under the terms of the GNU General Public License as published by | |
10 the Free Software Foundation, either version 3 of the License, or | |
11 (at your option) any later version. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 """ | |
21 | |
22 | |
23 from logging import debug, info, warning, error | |
24 from twisted.enterprise import adbapi | |
25 from twisted.internet import defer | |
26 import os.path | |
425
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
27 import time |
412 | 28 |
29 class SqliteStorage(): | |
30 """This class manage storage with Sqlite database""" | |
31 | |
32 | |
33 def __init__(self, db_filename): | |
34 """Connect to the given database | |
35 @param db_filename: full path to the Sqlite database""" | |
36 self.initialized = defer.Deferred() #triggered when memory is fully initialised and ready | |
37 init_defers = [] #list of deferred we have to wait to before initialisation is complete | |
38 self.profiles={} #we keep cache for the profiles (key: profile name, value: profile id) | |
39 | |
40 info(_("Connecting database")) | |
41 new_base = not os.path.exists(db_filename) #do we have to create the database ? | |
42 self.dbpool = adbapi.ConnectionPool("sqlite3", db_filename, check_same_thread=False) | |
43 init_defers.append(self.dbpool.runOperation("PRAGMA foreign_keys = ON").addErrback(lambda x: error(_("Can't activate foreign keys")))) | |
44 if new_base: | |
45 info(_("The database is new, creating the tables")) | |
46 database_creation = [ | |
47 "CREATE TABLE profiles (id INTEGER PRIMARY KEY ASC, name TEXT, UNIQUE (name))", | |
425
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
48 "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, FOREIGN KEY(profile_id) REFERENCES profiles(id))", |
412 | 49 "CREATE TABLE param_gen (category TEXT, name TEXT, value TEXT, PRIMARY KEY (category,name))", |
50 "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))"] | |
51 for op in database_creation: | |
52 d = self.dbpool.runOperation(op) | |
53 d.addErrback(lambda x: error(_("Error while creating tables in database [QUERY: %s]") % op )) | |
54 init_defers.append(d) | |
55 | |
56 def fillProfileCache(ignore): | |
57 d = self.dbpool.runQuery("SELECT name,id FROM profiles").addCallback(self._profilesCache) | |
58 d.chainDeferred(self.initialized) | |
59 | |
60 defer.DeferredList(init_defers).addCallback(fillProfileCache) | |
61 | |
62 #Profiles | |
63 def _profilesCache(self, profiles_result): | |
64 """Fill the profiles cache | |
65 @param profiles_result: result of the sql profiles query""" | |
66 for profile in profiles_result: | |
67 name, id = profile | |
68 self.profiles[name] = id | |
69 | |
70 def getProfilesList(self): | |
71 """"Return list of all registered profiles""" | |
72 return self.profiles.keys() | |
73 | |
74 def hasProfile(self, profile_name): | |
75 """return True if profile_name exists | |
76 @param profile_name: name of the profile to check""" | |
77 return self.profiles.has_key(profile_name) | |
78 | |
79 def createProfile(self, name): | |
80 """Create a new profile | |
81 @param name: name of the profile | |
82 @return: deferred triggered once profile is actually created""" | |
83 def getProfileId(ignore): | |
84 return self.dbpool.runQuery("SELECT (id) FROM profiles WHERE name = ?", (name,)) | |
85 | |
86 def profile_created(profile_id): | |
87 _id = profile_id[0][0] | |
88 self.profiles[name] = _id #we synchronise the cache | |
89 | |
90 d = self.dbpool.runQuery("INSERT INTO profiles(name) VALUES (?)", (name,)) | |
91 d.addCallback(getProfileId) | |
92 d.addCallback(profile_created) | |
93 return d | |
420
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
94 |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
95 def deleteProfile(self, name): |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
96 """Delete profile |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
97 @param name: name of the profile |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
98 @return: deferred triggered once profile is actually deleted""" |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
99 def deletionError(failure): |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
100 error(_("Can't delete profile [%s]") % name) |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
101 return failure |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
102 del self.profiles[name] |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
103 d = self.dbpool.runQuery("DELETE FROM profiles WHERE name = ?", (name,)) |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
104 d.addCallback(lambda ignore: info(_("Profile [%s] deleted") % name)) |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
105 return d |
acd908528ef7
core: profile creation/deletion through database
Goffi <goffi@goffi.org>
parents:
416
diff
changeset
|
106 |
412 | 107 |
108 #Params | |
109 def loadGenParams(self, params_gen): | |
110 """Load general parameters | |
111 @param params_gen: dictionary to fill | |
112 @return: deferred""" | |
113 def fillParams(result): | |
413
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
114 for param in result: |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
115 category,name,value = param |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
116 params_gen[(category, name)] = value |
412 | 117 debug(_("loading general parameters from database")) |
118 return self.dbpool.runQuery("SELECT category,name,value FROM param_gen").addCallback(fillParams) | |
119 | |
120 def loadIndParams(self, params_ind, profile): | |
121 """Load general parameters | |
122 @param params_ind: dictionary to fill | |
123 @param profile: a profile which *must* exist | |
124 @return: deferred""" | |
125 def fillParams(result): | |
413
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
126 for param in result: |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
127 category,name,value = param |
423
6c20c76abdcc
backend: - bridge async D-Bus method now automatically manage callback and errback, we just have to return a deferred
Goffi <goffi@goffi.org>
parents:
420
diff
changeset
|
128 params_ind[(category, name)] = value |
412 | 129 debug(_("loading individual parameters from database")) |
416
32dc8b18c2ae
core: param loading/purging on profile connection/disconnection
Goffi <goffi@goffi.org>
parents:
413
diff
changeset
|
130 d = self.dbpool.runQuery("SELECT category,name,value FROM param_ind WHERE profile_id=?", (self.profiles[profile],)) |
412 | 131 d.addCallback(fillParams) |
132 return d | |
133 | |
413
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
134 def getIndParam(self, category, name, profile): |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
135 """Ask database for the value of one specific individual parameter |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
136 @param category: category of the parameter |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
137 @param name: name of the parameter |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
138 @param profile: %(doc_profile)s |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
139 @return: deferred""" |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
140 d = self.dbpool.runQuery("SELECT value FROM param_ind WHERE category=? AND name=? AND profile_id=?", (category,name,self.profiles[profile])) |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
141 d.addCallback(self.__getFirstResult) |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
142 return d |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
143 |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
144 |
412 | 145 def setGenParam(self, category, name, value): |
146 """Save the general parameters in database | |
147 @param category: category of the parameter | |
148 @param name: name of the parameter | |
149 @param value: value to set | |
150 @return: deferred""" | |
151 d = self.dbpool.runQuery("REPLACE INTO param_gen(category,name,value) VALUES (?,?,?)", (category, name, value)) | |
152 d.addErrback(lambda ignore: error(_("Can't set general parameter (%(category)s/%(name)s) in database" % {"category":category, "name":name}))) | |
153 return d | |
154 | |
155 def setIndParam(self, category, name, value, profile): | |
156 """Save the general parameters in database | |
157 @param category: category of the parameter | |
158 @param name: name of the parameter | |
159 @param value: value to set | |
160 @param profile: a profile which *must* exist | |
161 @return: deferred""" | |
162 d = self.dbpool.runQuery("REPLACE INTO param_ind(category,name,profile_id,value) VALUES (?,?,?,?)", (category, name, self.profiles[profile], value)) | |
163 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}))) | |
164 return d | |
413
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
165 |
425
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
166 #History |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
167 def addToHistory(self, from_jid, to_jid, message, timestamp=None, profile=None): |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
168 """Store a new message in history |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
169 @param from_jid: full source JID |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
170 @param to_jid: full dest JID |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
171 @param message: message |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
172 @param timestamp: timestamp in seconds since epoch, or None to use current time |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
173 """ |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
174 assert(profile!=None) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
175 d = self.dbpool.runQuery("INSERT INTO history(source, source_res, dest, dest_res, timestamp, message, profile_id) VALUES (?,?,?,?,?,?,?)", |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
176 (from_jid.userhost(), from_jid.resource, to_jid.userhost(), to_jid.resource, timestamp or int(time.time()), |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
177 message, self.profiles[profile])) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
178 d.addErrback(lambda ignore: error(_("Can't save following message in history: from [%(from_jid)s] to [%(to_jid)s] ==> [%(message)s]" % |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
179 {"from_jid":from_jid.full(), "to_jid":to_jid.full(), "message":message}))) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
180 return d |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
181 |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
182 def getHistory(self, from_jid, to_jid, limit=0, between=True): |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
183 """Store a new message in history |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
184 @param from_jid: source JID (full, or bare for catchall |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
185 @param to_jid: dest JID (full, or bare for catchall |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
186 @param size: maximum number of messages to get, or 0 for unlimited |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
187 """ |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
188 def sqliteToDict(result): |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
189 result_dict = {} |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
190 for row in result: |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
191 timestamp, source, source_res, dest, dest_res, message = row |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
192 result_dict[timestamp] = ("%s/%s" % (source, source_res) if source_res else source, |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
193 "%s/%s" % (dest, dest_res) if dest_res else dest, |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
194 message) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
195 return result_dict |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
196 |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
197 query_parts = ["SELECT timestamp, source, source_res, dest, dest_res, message FROM history WHERE"] |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
198 values = [] |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
199 |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
200 if between: |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
201 query_parts.append("(source=? OR source=?) AND (dest=? or dest=?)") |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
202 values.extend([from_jid.userhost(), to_jid.userhost(), to_jid.userhost(), from_jid.userhost()]) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
203 else: |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
204 query_parts.append("source=? AND dest=?") |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
205 values.extend([from_jid.userhost(), to_jid.userhost()]) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
206 if from_jid.resource: |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
207 query_parts.append("AND source_res=?") |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
208 values.append(from_jid.resource) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
209 if to_jid.resource: |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
210 query_parts.append("AND dest_res=?") |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
211 values.append(to_jid.resource) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
212 if limit: |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
213 query_parts.append("LIMIT ?") |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
214 values.append(limit) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
215 d = self.dbpool.runQuery(" ".join(query_parts), values) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
216 return d.addCallback(sqliteToDict) |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
217 |
e4e9187e3b5b
backend, bridge: asynchronous history
Goffi <goffi@goffi.org>
parents:
423
diff
changeset
|
218 |
413
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
219 ##Helper methods## |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
220 |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
221 def __getFirstResult(self, result): |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
222 """Return the first result of a database query |
dd4caab17008
core: - individual parameters managed through sqlite
Goffi <goffi@goffi.org>
parents:
412
diff
changeset
|
223 Useful when we are looking for one specific value""" |
416
32dc8b18c2ae
core: param loading/purging on profile connection/disconnection
Goffi <goffi@goffi.org>
parents:
413
diff
changeset
|
224 return None if not result else result[0][0] |