changeset 484:23cbdf0a0777

core: presence status + last resource refactored and kept in entitiesCache in memory.py, profile cache is purged on disconnection
author Goffi <goffi@goffi.org>
date Wed, 15 Aug 2012 15:50:46 +0200 (2012-08-15)
parents e0d1eed4a46b
children ee95ff721b68
files src/core/sat_main.py src/core/xmpp.py src/memory/memory.py src/plugins/plugin_xep_0277.py src/test/helpers.py
diffstat 5 files changed, 63 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/sat_main.py	Thu Aug 02 01:08:51 2012 +0200
+++ b/src/core/sat_main.py	Wed Aug 15 15:50:46 2012 +0200
@@ -256,6 +256,7 @@
 
             return current.getConnectionDeferred()
 
+        self.memory.startProfileSession(profile)
         return self.memory.loadIndividualParams(profile).addCallback(afterMemoryInit)
 
     def disconnect(self, profile_key):
@@ -290,7 +291,7 @@
             del self.profiles[profile]
         except KeyError:
             error(_("Trying to remove reference to a client not referenced"))
-        self.memory.purgeProfile(profile)
+        self.memory.purgeProfileSession(profile)
 
     def startService(self):
         info("Salut à toi ô mon frère !")
--- a/src/core/xmpp.py	Thu Aug 02 01:08:51 2012 +0200
+++ b/src/core/xmpp.py	Wed Aug 15 15:50:46 2012 +0200
@@ -249,7 +249,7 @@
             statuses["default"] = statuses[None]
             del statuses[None]
 
-        self.host.memory.addPresenceStatus(entity, show or "",
+        self.host.memory.setPresenceStatus(entity, show or "",
                 int(priority), statuses, self.parent.profile)
 
         #now it's time to notify frontends
@@ -265,7 +265,7 @@
         if statuses.has_key(None):   #we only want string keys
             statuses["default"] = statuses[None]
             del statuses[None]
-        self.host.memory.addPresenceStatus(entity, "unavailable", 0, statuses, self.parent.profile)
+        self.host.memory.setPresenceStatus(entity, "unavailable", 0, statuses, self.parent.profile)
 
         #now it's time to notify frontends
         self.host.bridge.presenceUpdate(entity.full(), "unavailable", 0, statuses, self.parent.profile)
--- a/src/memory/memory.py	Thu Aug 02 01:08:51 2012 +0200
+++ b/src/memory/memory.py	Wed Aug 15 15:50:46 2012 +0200
@@ -299,7 +299,7 @@
             d = self.storage.getIndParam(category, name, profile)
             return d.addCallback(lambda value: value if value!=None else default)
 
-    def __getParam(self, profile, category, name, type='individual', cache=None):
+    def __getParam(self, profile, category, name, _type='individual', cache=None):
         """Return the param, or None if it doesn't exist
         @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@)
         @param category: param category
@@ -308,11 +308,11 @@
         @param cache: temporary cache, to use when profile is not logged
         @return: param value or None if it doesn't exist
         """
-        if type == 'general':
+        if _type == 'general':
             if self.params_gen.has_key((category, name)):
                 return self.params_gen[(category, name)]
             return None  #This general param has the default value
-        assert (type == 'individual')
+        assert (_type == 'individual')
         if self.params.has_key(profile):
             cache = self.params[profile] # if profile is in main cache, we use it,
                                          # ignoring the temporary cache
@@ -420,19 +420,19 @@
         d = self.__constructProfileXml(profile)
         return d.addCallback(returnCategoryXml)
 
-    def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ?
+    def __getParamNode(self, name, category, _type="@ALL@"): #FIXME: is _type useful ?
         """Return a node from the param_xml
         @param name: name of the node
         @param category: category of the node
-        @type: keyword for search:
+        @_type: keyword for search:
                                     @ALL@ search everywhere
                                     @GENERAL@ only search in general type
                                     @INDIVIDUAL@ only search in individual type
         @return: a tuple with the node type and the the node, or None if not found"""
 
         for type_node in self.dom.documentElement.childNodes:
-            if ( ((type == "@ALL@" or type == "@GENERAL@") and type_node.nodeName == 'general') 
-            or ( (type == "@ALL@" or type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ):
+            if ( ((_type == "@ALL@" or _type == "@GENERAL@") and type_node.nodeName == 'general') 
+            or ( (_type == "@ALL@" or _type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ):
                 for node in type_node.getElementsByTagName('category'):
                     if node.getAttribute("name") == category:
                         params = node.getElementsByTagName("param")
@@ -475,8 +475,8 @@
         assert (node[0] == 'individual')
         assert (profile_key != "@NONE@")
         
-        type = node[1].getAttribute("type")
-        if type=="button":
+        _type = node[1].getAttribute("type")
+        if _type=="button":
             print "clique",node.toxml()
         else:
             if self.host.isConnected(profile): #key can not exists if profile is not connected
@@ -491,8 +491,8 @@
         info (_("Memory manager init"))
         self.initialized = defer.Deferred() 
         self.host = host
-        self.presenceStatus={}
-        self.lastResource={} #tmp, will be refactored with bdd integration
+        self.entitiesCache={} #XXX: keep presence/last resource/other data in cache
+                              #     /!\ an entity is not necessarily in roster
         self.subscriptions={}
         self.server_features={} #used to store discovery's informations
         self.server_identities={}
@@ -562,10 +562,22 @@
         @param profile: %(doc_profile)s"""
         return self.params.loadIndParams(profile)
 
-    def purgeProfile(self, profile):
+    def startProfileSession(self, profile):
+        """"Iniatialise session for a profile
+        @param profile: %(doc_profile)s"""
+        info(_("[%s] Profile session started" % profile))
+        self.entitiesCache[profile] = {}
+    
+    def purgeProfileSession(self, profile):
         """Delete cache of data of profile
         @param profile: %(doc_profile)s"""
+        info(_("[%s] Profile session purge" % profile))
         self.params.purgeProfile(profile)
+        try:
+            del self.entitiesCache[profile]
+        except KeyError:
+            error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile)
+
 
     def save(self):
         """Save parameters and all memory things to file/db"""
@@ -618,29 +630,29 @@
             self.server_features[profile] = []
         self.server_features[profile].append(feature)
     
-    def addServerIdentity(self, category, type, entity, profile):
+    def addServerIdentity(self, category, _type, entity, profile):
         """Add an identity discovered from server
         @param feature: string of the feature
         @param profile: which profile is using this server ?"""
         if not self.server_identities.has_key(profile):
             self.server_identities[profile] = {}
-        if not self.server_identities[profile].has_key((category, type)):
-            self.server_identities[profile][(category, type)]=set()
-        self.server_identities[profile][(category, type)].add(entity)
+        if not self.server_identities[profile].has_key((category, _type)):
+            self.server_identities[profile][(category, _type)]=set()
+        self.server_identities[profile][(category, _type)].add(entity)
 
-    def getServerServiceEntities(self, category, type, profile):
+    def getServerServiceEntities(self, category, _type, profile):
         """Return all available entities for a service"""
         if self.server_identities.has_key(profile):
-            return self.server_identities[profile].get((category, type), set())
+            return self.server_identities[profile].get((category, _type), set())
         else:
             return None
 
-    def getServerServiceEntity(self, category, type, profile):
+    def getServerServiceEntity(self, category, _type, profile):
         """Helper method to get first available entity for a service"""
-        entities = self.getServerServiceEntities(category, type, profile)
+        entities = self.getServerServiceEntities(category, _type, profile)
         if entities == None:
             warning(_("Entities (%(category)s/%(type)s) not available, maybe they haven't been asked to server yet ?") % {"category":category,
-                                                                                                                          "type":type})
+                                                                                                                          "type":_type})
             return None
         else:
             return list(entities)[0] if entities else None
@@ -659,38 +671,40 @@
         @param contact: contact jid (unicode)
         @param profile_key: %(doc_profile_key)s"""
         profile = self.getProfileName(profile_key)
-        if not profile:
-            error(_('Asking contacts for a non-existant profile'))
+        if not profile or not self.host.isConnected(profile):
+            error(_('Asking contacts for a non-existant or not connected profile'))
+            return ""
+        entity = jid.JID(contact).userhost()
+        if not entity in self.entitiesCache[profile]:
+            info(_("Entity not in cache"))
             return ""
         try:
-            return self.lastResource[profile][jid.JID(contact).userhost()]
-        except:
+            return self.entitiesCache[profile][entity]["last_resource"]
+        except KeyError:
             return ""
     
-    def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key):
+    def setPresenceStatus(self, contact_jid, show, priority, statuses, profile_key):
+        """Change the presence status of an entity"""
         profile = self.getProfileName(profile_key)
         if not profile:
             error(_('Trying to add presence status to a non-existant profile'))
             return
-        if not self.presenceStatus.has_key(profile):
-            self.presenceStatus[profile] = {}
-        if not self.lastResource.has_key(profile):
-            self.lastResource[profile] = {}
-        if not self.presenceStatus[profile].has_key(contact_jid.userhost()):
-            self.presenceStatus[profile][contact_jid.userhost()] = {}
+        entity_data = self.entitiesCache[profile].setdefault(contact_jid.userhost(),{})
         resource = jid.parse(contact_jid.full())[2] or ''
         if resource:
-            self.lastResource[profile][contact_jid.userhost()] = resource
+            entity_data["last_resource"] = resource
+        if not "last_resource" in entity_data:
+            entity_data["last_resource"] = ''
 
-        self.presenceStatus[profile][contact_jid.userhost()][resource] = (show, priority, statuses)
+        entity_data.setdefault("presence",{})[resource] = (show, priority, statuses)
 
-    def addWaitingSub(self, type, contact_jid, profile_key):
+    def addWaitingSub(self, _type, contact_jid, profile_key):
         """Called when a subcription request is received"""
         profile = self.getProfileName(profile_key)
         assert(profile)
         if not self.subscriptions.has_key(profile):
             self.subscriptions[profile] = {}
-        self.subscriptions[profile][contact_jid] = type
+        self.subscriptions[profile][contact_jid] = _type
     
     def delWaitingSub(self, contact_jid, profile_key):
         """Called when a subcription request is finished"""
@@ -715,10 +729,13 @@
         if not profile:
             error(_('Asking contacts for a non-existant profile'))
             return {}
-        if not self.presenceStatus.has_key(profile):
-            self.presenceStatus[profile] = {}
-        debug ("Memory getPresenceStatus (%s)", self.presenceStatus[profile])
-        return self.presenceStatus[profile]
+        entities_presence = {}
+        for entity in self.entitiesCache[profile]:
+            # if entity exists, "presence" key must exist
+            entities_presence[entity] = self.entitiesCache[profile][entity]["presence"]
+
+        debug ("Memory getPresenceStatus (%s)", entities_presence)
+        return entities_presence
 
     def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'):
         return self.params.getParamA(name, category, attr, profile_key)
--- a/src/plugins/plugin_xep_0277.py	Thu Aug 02 01:08:51 2012 +0200
+++ b/src/plugins/plugin_xep_0277.py	Wed Aug 15 15:50:46 2012 +0200
@@ -92,7 +92,7 @@
                 microblog_data['author'] = _entry.authors[0].name.text
             microblog_data['timestamp'] = str(int(_entry.updated.tf))
             microblog_data['id'] = item['id']
-        except AttributeError, KeyError:
+        except (AttributeError, KeyError):
             error(_('Error while parsing atom entry for microblogging event'))
             return {}
 
--- a/src/test/helpers.py	Thu Aug 02 01:08:51 2012 +0200
+++ b/src/test/helpers.py	Wed Aug 15 15:50:46 2012 +0200
@@ -66,7 +66,7 @@
     def addContact(self, contact_jid, attributes, groups, profile_key='@DEFAULT@'):
         pass
     
-    def addPresenceStatus(self, contact_jid, show, priority, statuses, profile_key='@DEFAULT@'):
+    def setPresenceStatus(self, contact_jid, show, priority, statuses, profile_key='@DEFAULT@'):
         pass
     
     def addWaitingSub(self, type, contact_jid, profile_key):