From 8624c3c547c955e68cc07b8d1a452fe15a4467c4 Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 7 Nov 2013 15:29:39 +0100 Subject: [PATCH] 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; --- frontends/src/quick_frontend/quick_chat.py | 2 +- src/bridge/DBus.py | 2 +- .../bridge_constructor/bridge_template.ini | 4 +-- src/core/xmpp.py | 4 +-- src/memory/memory.py | 6 ++-- src/memory/sqlite.py | 31 ++++++++++++------- src/test/helpers.py | 2 +- 7 files changed, 30 insertions(+), 21 deletions(-) diff --git a/frontends/src/quick_frontend/quick_chat.py b/frontends/src/quick_frontend/quick_chat.py index 850fe465..5ec4f528 100644 --- a/frontends/src/quick_frontend/quick_chat.py +++ b/frontends/src/quick_frontend/quick_chat.py @@ -98,7 +98,7 @@ class QuickChat(object): debug (_("now we print history")) def onHistory(history): for line in history: - timestamp, from_jid, to_jid, message, _type = line + timestamp, from_jid, to_jid, message, _type, extra = line if ((self.type == 'group' and _type != 'groupchat') or (self.type == 'one2one' and _type == 'groupchat')): continue diff --git a/src/bridge/DBus.py b/src/bridge/DBus.py index 27f6b5ee..31589332 100755 --- a/src/bridge/DBus.py +++ b/src/bridge/DBus.py @@ -271,7 +271,7 @@ class DbusObject(dbus.service.Object): return self._callback("getEntityData", unicode(jid), keys, unicode(profile)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, - in_signature='ssibs', out_signature='a(dssss)', + in_signature='ssibs', out_signature='a(dssssa{ss})', async_callbacks=('callback', 'errback')) def getHistory(self, from_jid, to_jid, limit, between=True, profile="@NONE@", callback=None, errback=None): return self._callback("getHistory", unicode(from_jid), unicode(to_jid), limit, between, unicode(profile), callback=callback, errback=errback) diff --git a/src/bridge/bridge_constructor/bridge_template.ini b/src/bridge/bridge_constructor/bridge_template.ini index 1d9571c4..091e51ba 100644 --- a/src/bridge/bridge_constructor/bridge_template.ini +++ b/src/bridge/bridge_constructor/bridge_template.ini @@ -488,7 +488,7 @@ async= type=method category=core sig_in=ssibs -sig_out=a(dssss) +sig_out=a(dssssa{ss}) param_3_default=True param_4_default="@NONE@" doc=Get history of a communication between two entities @@ -497,7 +497,7 @@ doc_param_1=to_jid: dest JID (bare jid for catch all, full jid else) doc_param_2=limit: max number of history elements to get (0 for the whole history) doc_param_3=between: True if we want history between the two jids (in both direction), False if we only want messages from from_jid to to_jid doc_param_4=%(doc_profile)s -doc_return=Ordered list (by timestamp) of tuples (timestamp, full from_jid, full to_jid, message, type) +doc_return=Ordered list (by timestamp) of tuples (timestamp, full from_jid, full to_jid, message, type, extra) [addContact] type=method diff --git a/src/core/xmpp.py b/src/core/xmpp.py index 6b11527e..eb6bdb67 100644 --- a/src/core/xmpp.py +++ b/src/core/xmpp.py @@ -138,9 +138,9 @@ class SatMessageProtocol(xmppim.MessageProtocol): data['extra']['archive'] = str(timestamp) if data['type'] != 'groupchat': # XXX: we don't save delayed messages in history for groupchats #TODO: add delayed messages to history if they aren't already in it - self.host.memory.addToHistory(jid.JID(data['from']), jid.JID(data['to']), data['body'], data['type'], timestamp, profile=self.parent.profile) + self.host.memory.addToHistory(jid.JID(data['from']), jid.JID(data['to']), data['body'], data['type'], data['extra'], timestamp, profile=self.parent.profile) except IndexError: - self.host.memory.addToHistory(jid.JID(data['from']), jid.JID(data['to']), data['body'], data['type'], profile=self.parent.profile) + self.host.memory.addToHistory(jid.JID(data['from']), jid.JID(data['to']), data['body'], data['type'], data['extra'], profile=self.parent.profile) self.host.bridge.newMessage(data['from'], data['body'], data['type'], data['to'], data['extra'], profile=self.parent.profile) post_treat.addCallback(after_treatments) diff --git a/src/memory/memory.py b/src/memory/memory.py index 578d49f7..fd5c83da 100644 --- a/src/memory/memory.py +++ b/src/memory/memory.py @@ -723,9 +723,11 @@ class Memory(object): @param name: Name of the profile""" return self.params.deleteProfile(name) - def addToHistory(self, from_jid, to_jid, message, _type='chat', timestamp=None, profile="@NONE@"): + def addToHistory(self, from_jid, to_jid, message, _type='chat', extra=None, timestamp=None, profile="@NONE@"): assert profile != "@NONE@" - return self.storage.addToHistory(from_jid, to_jid, message, _type, timestamp, profile) + if extra is None: + extra = {} + return self.storage.addToHistory(from_jid, to_jid, message, _type, extra, timestamp, profile) def getHistory(self, from_jid, to_jid, limit=0, between=True, profile="@NONE@"): assert profile != "@NONE@" diff --git a/src/memory/sqlite.py b/src/memory/sqlite.py index 0c159f21..26f8ee47 100644 --- a/src/memory/sqlite.py +++ b/src/memory/sqlite.py @@ -51,7 +51,7 @@ class SqliteStorage(object): "INSERT INTO message_types VALUES ('groupchat')", "INSERT INTO message_types VALUES ('headline')", "INSERT INTO message_types VALUES ('normal')", - "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))", + "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))", "CREATE TABLE param_gen (category TEXT, name TEXT, value TEXT, PRIMARY KEY (category,name))", "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)", "CREATE TABLE private_gen (namespace TEXT, key TEXT, value TEXT, PRIMARY KEY (namespace, key))", @@ -177,18 +177,21 @@ class SqliteStorage(object): return d #History - def addToHistory(self, from_jid, to_jid, message, _type='chat', timestamp=None, profile=None): + def addToHistory(self, from_jid, to_jid, message, _type='chat', extra=None, timestamp=None, profile=None): """Store a new message in history @param from_jid: full source JID @param to_jid: full dest JID @param message: message @param _type: message type (see RFC 6121 ยง5.2.2) + @param extra: dictionary (keys and values are unicode) of extra message data @param timestamp: timestamp in seconds since epoch, or None to use current time """ assert(profile) - d = self.dbpool.runQuery("INSERT INTO history(source, source_res, dest, dest_res, timestamp, message, type, profile_id) VALUES (?,?,?,?,?,?,?,?)", + if extra is None: + extra = {} + d = self.dbpool.runQuery("INSERT INTO history(source, source_res, dest, dest_res, timestamp, message, type, extra, profile_id) VALUES (?,?,?,?,?,?,?,?,?)", (from_jid.userhost(), from_jid.resource, to_jid.userhost(), to_jid.resource, timestamp or time(), - message, _type, self.profiles[profile])) + message, _type, pickle.dumps(extra, 0), self.profiles[profile])) d.addErrback(lambda ignore: error(_("Can't save following message in history: from [%(from_jid)s] to [%(to_jid)s] ==> [%(message)s]" % {"from_jid": from_jid.full(), "to_jid": to_jid.full(), "message": message}))) return d @@ -201,25 +204,29 @@ class SqliteStorage(object): """ assert(profile) - def sqliteToDict(query_result): + def sqliteToList(query_result): query_result.reverse() result = [] for row in query_result: - timestamp, source, source_res, dest, dest_res, message, _type = row + timestamp, source, source_res, dest, dest_res, message, type_, extra_raw = row + try: + extra = pickle.loads(str(extra_raw or "")) + except EOFError: + extra = {} result.append((timestamp, "%s/%s" % (source, source_res) if source_res else source, "%s/%s" % (dest, dest_res) if dest_res else dest, - message, _type)) + message, type_, extra)) return result - query_parts = ["SELECT timestamp, source, source_res, dest, dest_res, message, type FROM history WHERE profile_id=? AND"] + query_parts = ["SELECT timestamp, source, source_res, dest, dest_res, message, type, extra FROM history WHERE profile_id=? AND"] values = [self.profiles[profile]] - def test_jid(_type, _jid): + def test_jid(type_, _jid): values.append(_jid.userhost()) if _jid.resource: values.append(_jid.resource) - return '(%s=? AND %s_res=?)' % (_type, _type) - return '%s=?' % (_type, ) + return '(%s=? AND %s_res=?)' % (type_, type_) + return '%s=?' % (type_, ) if between: query_parts.append("(%s OR %s) AND (%s or %s)" % (test_jid('source', from_jid), @@ -237,7 +244,7 @@ class SqliteStorage(object): values.append(limit) d = self.dbpool.runQuery(" ".join(query_parts), values) - return d.addCallback(sqliteToDict) + return d.addCallback(sqliteToList) #Private values def loadGenPrivates(self, private_gen, namespace): diff --git a/src/test/helpers.py b/src/test/helpers.py index 5371dcff..08b785a6 100644 --- a/src/test/helpers.py +++ b/src/test/helpers.py @@ -74,7 +74,7 @@ class FakeMemory(object): def getProfileName(self, profile_key): return profile_key - def addToHistory(self, from_jid, to_jid, message, _type='chat', timestamp=None, profile=None): + def addToHistory(self, from_jid, to_jid, message, _type='chat', extra=None, timestamp=None, profile="@NONE@"): pass def addContact(self, contact_jid, attributes, groups, profile_key='@DEFAULT@'):