changeset 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 7bb50096d225
children 0fd123340fb9
files frontends/src/quick_frontend/quick_chat.py src/bridge/DBus.py src/bridge/bridge_constructor/bridge_template.ini src/core/xmpp.py src/memory/memory.py src/memory/sqlite.py src/test/helpers.py
diffstat 7 files changed, 30 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/quick_frontend/quick_chat.py	Tue Nov 05 22:41:45 2013 +0100
+++ b/frontends/src/quick_frontend/quick_chat.py	Thu Nov 07 15:29:39 2013 +0100
@@ -98,7 +98,7 @@
         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
--- a/src/bridge/DBus.py	Tue Nov 05 22:41:45 2013 +0100
+++ b/src/bridge/DBus.py	Thu Nov 07 15:29:39 2013 +0100
@@ -271,7 +271,7 @@
         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)
--- a/src/bridge/bridge_constructor/bridge_template.ini	Tue Nov 05 22:41:45 2013 +0100
+++ b/src/bridge/bridge_constructor/bridge_template.ini	Thu Nov 07 15:29:39 2013 +0100
@@ -488,7 +488,7 @@
 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_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
--- a/src/core/xmpp.py	Tue Nov 05 22:41:45 2013 +0100
+++ b/src/core/xmpp.py	Thu Nov 07 15:29:39 2013 +0100
@@ -138,9 +138,9 @@
                 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)
--- a/src/memory/memory.py	Tue Nov 05 22:41:45 2013 +0100
+++ b/src/memory/memory.py	Thu Nov 07 15:29:39 2013 +0100
@@ -723,9 +723,11 @@
         @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@"
--- a/src/memory/sqlite.py	Tue Nov 05 22:41:45 2013 +0100
+++ b/src/memory/sqlite.py	Thu Nov 07 15:29:39 2013 +0100
@@ -51,7 +51,7 @@
                 "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 @@
         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 @@
         """
         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 @@
             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):
--- a/src/test/helpers.py	Tue Nov 05 22:41:45 2013 +0100
+++ b/src/test/helpers.py	Thu Nov 07 15:29:39 2013 +0100
@@ -74,7 +74,7 @@
     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@'):