changeset 413:dd4caab17008

core: - individual parameters managed through sqlite - new asyncGetParamA method, which allow to get parameter for a not connected profile ==> individual parameters are cached in memory when the profile is connected, but must be accessed though sqlite else, and that must be done asynchronously primitivus: - profile manager updated to use asyncGetParamA /!\ still broken, need more work before being able to run again
author Goffi <goffi@goffi.org>
date Tue, 01 Nov 2011 20:39:22 +0100
parents 62b17854254e
children f6f94e21c642
files frontends/src/bridge/DBus.py frontends/src/primitivus/profile_manager.py src/bridge/DBus.py src/bridge/bridge_constructor/bridge_template.ini src/core/sat_main.py src/tools/memory.py src/tools/sqlite.py
diffstat 7 files changed, 134 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/bridge/DBus.py	Sun Oct 30 23:13:40 2011 +0100
+++ b/frontends/src/bridge/DBus.py	Tue Nov 01 20:39:22 2011 +0100
@@ -62,6 +62,9 @@
     def asyncConnect(self, profile_key="@DEFAULT@", callback=None, errback=None):
         return self.db_core_iface.asyncConnect(profile_key, reply_handler=callback, error_handler=errback)
 
+    def asyncGetParamA(self, name, category, attribute="value", profile_key="@DEFAULT@", callback=None, errback=None):
+        return unicode(self.db_core_iface.asyncGetParamA(name, category, attribute, profile_key, reply_handler=callback, error_handler=errback))
+
     def callMenu(self, category, name, menu_type, profile_key):
         return unicode(self.db_core_iface.callMenu(category, name, menu_type, profile_key))
 
--- a/frontends/src/primitivus/profile_manager.py	Sun Oct 30 23:13:40 2011 +0100
+++ b/frontends/src/primitivus/profile_manager.py	Tue Nov 01 20:39:22 2011 +0100
@@ -91,12 +91,15 @@
         self.host.showPopUp(pop_up_widget)
 
     def onProfileChange(self, list_wid):
+        def setJID(jabberID):
+            self.login_wid.set_edit_text(jabberID)
+        def setPassword(password):
+            self.pass_wid.set_edit_text(password)
+
         profile_name = list_wid.getSelectedValue()
         if profile_name:
-            jabberID = self.host.bridge.getParamA("JabberID", "Connection", profile_key=profile_name)
-            password = self.host.bridge.getParamA("Password", "Connection", profile_key=profile_name)
-            self.login_wid.set_edit_text(jabberID)
-            self.pass_wid.set_edit_text(password)
+            self.host.bridge.asyncGetParamA("JabberID", "Connection", profile_key=profile_name, callback=setJID, errback=self.getParamError)
+            self.host.bridge.asyncGetParamA("Password", "Connection", profile_key=profile_name, callback=setPassword, errback=self.getParamError)
         
     def onConnectProfile(self, button):
         profile_name = self.list_profile.getSelectedValue()
@@ -110,15 +113,24 @@
             profile = self.host.bridge.getProfileName(profile_name)
             assert(profile)
             #TODO: move this to quick_app
-            old_jid = self.host.bridge.getParamA("JabberID", "Connection", profile_key=profile)
-            old_pass = self.host.bridge.getParamA("Password", "Connection", profile_key=profile)
-            new_jid = self.login_wid.get_edit_text()
-            new_pass = self.pass_wid.get_edit_text()
+            self.host.bridge.asyncGetParamA("JabberID", "Connection", profile_key=profile,
+                                            callback=lambda old_jid: self.__old_jidReceived(old_jid, profile), errback=self.getParamError)
 
-            if old_jid != new_jid:
-                self.host.bridge.setParam("JabberID", new_jid, "Connection", profile)
-                self.host.bridge.setParam("Server", JID(new_jid).domain, "Connection", profile)
-            if old_pass != new_pass:
-                self.host.bridge.setParam("Password", new_pass, "Connection", profile)
-            self.host.plug_profile(profile)
+    def __old_jidReceived(self, old_jid, profile):
+        self.host.bridge.asyncGetParamA("Password", "Connection", profile_key=profile,
+                                        callback=lambda old_pass: self.__old_passReceived(old_jid, old_pass, profile), errback=self.getParamError)
+    
+    def __old_passReceived(self, old_jid, old_pass, profile):
+        """Check if we have new jid/pass, save them if it is the case, and plug profile"""
+        new_jid = self.login_wid.get_edit_text()
+        new_pass = self.pass_wid.get_edit_text()
+
+        if old_jid != new_jid:
+            self.host.bridge.setParam("JabberID", new_jid, "Connection", profile)
+            self.host.bridge.setParam("Server", JID(new_jid).domain, "Connection", profile)
+        if old_pass != new_pass:
+            self.host.bridge.setParam("Password", new_pass, "Connection", profile)
+        self.host.plug_profile(profile)
             
+    def getParamError(self):
+        error(_("Can't get profile parameter"))
--- a/src/bridge/DBus.py	Sun Oct 30 23:13:40 2011 +0100
+++ b/src/bridge/DBus.py	Tue Nov 01 20:39:22 2011 +0100
@@ -138,6 +138,12 @@
 
     @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
                          in_signature='ssss', out_signature='s',
+                         async_callbacks=('callback', 'errback'))
+    def asyncGetParamA(self, name, category, attribute="value", profile_key="@DEFAULT@", callback=None, errback=None):
+        return self.cb["asyncGetParamA"](unicode(name), unicode(category), unicode(attribute), unicode(profile_key), callback, errback)
+
+    @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
+                         in_signature='ssss', out_signature='s',
                          async_callbacks=None)
     def callMenu(self, category, name, menu_type, profile_key):
         return self.cb["callMenu"](unicode(category), unicode(name), unicode(menu_type), unicode(profile_key))
--- a/src/bridge/bridge_constructor/bridge_template.ini	Sun Oct 30 23:13:40 2011 +0100
+++ b/src/bridge/bridge_constructor/bridge_template.ini	Tue Nov 01 20:39:22 2011 +0100
@@ -360,6 +360,20 @@
 sig_out=s
 param_2_default="value"
 param_3_default="@DEFAULT@"
+doc=Helper method to get a parameter's attribute *when profile is connected*
+doc_param_0=name: as for [setParam]
+doc_param_1=category: as for [setParam]
+doc_param_2=attribute: Name of the attribute
+doc_param_3=%(doc_profile_key)s
+
+[asyncGetParamA]
+async=
+type=method
+category=core
+sig_in=ssss
+sig_out=s
+param_2_default="value"
+param_3_default="@DEFAULT@"
 doc=Helper method to get a parameter's attribute
 doc_param_0=name: as for [setParam]
 doc_param_1=category: as for [setParam]
--- a/src/core/sat_main.py	Sun Oct 30 23:13:40 2011 +0100
+++ b/src/core/sat_main.py	Tue Nov 01 20:39:22 2011 +0100
@@ -130,6 +130,7 @@
         self.bridge.register("getConfig", self.memory.getConfig)
         self.bridge.register("setParam", self.setParam)
         self.bridge.register("getParamA", self.memory.getParamA)
+        self.bridge.register("asyncGetParamA", self.memory.asyncGetParamA)
         self.bridge.register("getParamsUI", self.memory.getParamsUI)
         self.bridge.register("getParams", self.memory.getParams)
         self.bridge.register("getParamsForCategory", self.memory.getParamsForCategory)
--- a/src/tools/memory.py	Sun Oct 30 23:13:40 2011 +0100
+++ b/src/tools/memory.py	Tue Nov 01 20:39:22 2011 +0100
@@ -39,6 +39,9 @@
 SAVEFILE_PRIVATE="/private"  #file used to store misc values (mainly for plugins)
 SAVEFILE_DATABASE="/sat.db"
 
+class ProfileNotInCacheError(Exception):
+    pass
+
 class Param():
     """This class manage parameters with xml"""
     ### TODO: add desciption in params
@@ -76,20 +79,18 @@
         """Load parameters template from file"""
         self.dom = minidom.parse(file)
     
-    def loadGenData(self, storage):
+    def loadGenData(self):
         """Load general parameters data from storage
-        @param storage: xxxStorage object instance
         @return: deferred triggered once params are loaded"""
-        return storage.loadGenParams(self.params_gen)
+        return self.storage.loadGenParams(self.params_gen)
         
-    def loadIndData(self, storage, profile):
+    def loadIndData(self, profile):
         """Load individual parameters
         set self.params cache
-        @param storage: xxxStorage object instance
         @param profile: profile to load (*must exist*)
         @return: deferred triggered once params are loaded"""
         self.params[profile] = {}
-        return storage.loadIndParams(self.params, profile)
+        return self.storage.loadIndParams(self.params, profile)
     
     def save_xml(self, file):
         """Save parameters template to xml file"""
@@ -109,9 +110,10 @@
         with open(file+'_ind', 'w') as param_ind_pickle:
             pickle.dump(self.params, param_ind_pickle)
 
-    def __init__(self, host):
+    def __init__(self, host, storage):
         debug("Parameters init")
         self.host = host
+        self.storage = storage
         self.default_profile = None
         self.params = {}
         self.params_gen = {}
@@ -227,7 +229,7 @@
         node = self.__getParamNode(name, category)
         if not node:
             error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category})
-            return None
+            return ""
 
         if node[0] == 'general':
             value = self.__getParam(None, category, name, 'general')
@@ -239,12 +241,53 @@
         if not profile:
             error(_('Requesting a param for an non-existant profile'))
             return ""
+        
+        if profile not in self.params:
+            error(_('Requesting synchronous param for not connected profile'))
+            return ""
 
         if attr == "value": 
             return self.__getParam(profile, category, name) or node[1].getAttribute(attr)
         else:
             return node[1].getAttribute(attr)
 
+    def asyncGetParamA(self, name, category, attr="value", profile_key="@DEFAULT@", callback=None, errback=None):
+        """Helper method to get a specific attribute
+           @param name: name of the parameter
+           @param category: category of the parameter
+           @param attr: name of the attribute (default: "value")
+           @param profile: owner of the param (@ALL@ for everyone)
+           @param callback: called when the profile is connected
+           @param errback: called is the connection fail"""
+        node = self.__getParamNode(name, category)
+        if not node:
+            error(_("Requested param [%(name)s] in category [%(category)s] doesn't exist !") % {'name':name, 'category':category})
+            return None
+
+        if node[0] == 'general':
+            value = self.__getParam(None, category, name, 'general')
+            callback(value or node[1].getAttribute(attr))
+            return
+        
+        assert(node[0] == 'individual')
+
+        profile = self.getProfileName(profile_key)
+        if not profile:
+            error(_('Requesting a param for a non-existant profile'))
+            errback()
+            return
+        
+        if attr != "value": 
+            callback(node[1].getAttribute(attr))
+            return
+        default = node[1].getAttribute(attr)
+        try:
+            callback(self.__getParam(profile, category, name) or default)
+        except ProfileNotInCacheError:
+            #We have to ask data to the storage manager
+            d = self.storage.getIndParam(category, name, profile)
+            d.addCallback(callback)
+            d.addErrback(lambda x:errback())
 
     def __getParam(self, profile, category, name, type='individual'):
         """Return the param, or None if it doesn't exist
@@ -257,7 +300,9 @@
                 return self.params_gen[(category, name)]
             return None  #This general param has the default value
         assert (type == 'individual')
-        if not self.params.has_key(profile) or not self.params[profile].has_key((category, name)):
+        if not self.params.has_key(profile):
+            raise ProfileNotInCacheError
+        if not self.params[profile].has_key((category, name)):
             return None
         return self.params[profile][(category, name)]
 
@@ -418,7 +463,6 @@
         self.presenceStatus={}
         self.lastResource={} #tmp, will be refactored with bdd integration
         self.subscriptions={}
-        self.params=Param(host)
         self.history={}  #used to store chat history (key: short jid)
         self.private={}  #used to store private value
         self.server_features={} #used to store discovery's informations
@@ -429,8 +473,9 @@
         host.set_const('savefile_database', SAVEFILE_DATABASE)
         database_file = os.path.expanduser(self.getConfig('','local_dir')+
                                         self.host.get_const('savefile_database'))
+        self.storage = SqliteStorage(database_file)
+        self.params=Param(host, self.storage)
         self.loadFiles()
-        self.storage = SqliteStorage(database_file)
         self.storage.initialized.addCallback(lambda ignore: self.load(init_defers))
         
         defer.DeferredList(init_defers).chainDeferred(self.initialized)
@@ -506,7 +551,7 @@
         """Load parameters and all memory things from db
         @param init_defers: list of deferred to wait before parameters are loaded"""
         #parameters data
-        init_defers.append(self.params.loadGenData(self.storage))
+        init_defers.append(self.params.loadGenData())
 
     def loadIndividualParams(self, profile_key):
         """Load individual parameters for a profile
@@ -764,6 +809,9 @@
     def getParamA(self, name, category, attr="value", profile_key='@DEFAULT@'):
         return self.params.getParamA(name, category, attr, profile_key)
     
+    def asyncGetParamA(self, name, category, attr="value", profile_key='@DEFAULT@', callback=None, errback=None):
+        return self.params.asyncGetParamA(name, category, attr, profile_key, callback, errback)
+    
     def getParamsUI(self, profile_key):
         return self.params.getParamsUI(profile_key)
   
--- a/src/tools/sqlite.py	Sun Oct 30 23:13:40 2011 +0100
+++ b/src/tools/sqlite.py	Tue Nov 01 20:39:22 2011 +0100
@@ -98,7 +98,9 @@
         @param params_gen: dictionary to fill
         @return: deferred"""
         def fillParams(result):
-            params_gen[(category, name)] = value
+            for param in result:
+                category,name,value = param
+                params_gen[(category, name)] = value
         debug(_("loading general parameters from database")) 
         return self.dbpool.runQuery("SELECT category,name,value FROM param_gen").addCallback(fillParams)
 
@@ -108,12 +110,25 @@
         @param profile: a profile which *must* exist
         @return: deferred"""
         def fillParams(result):
-            params_ind[profile][(category, name)] = value
+            for param in result:
+                category,name,value = param
+                params_ind[profile][(category, name)] = value
         debug(_("loading individual parameters from database")) 
         d = self.dbpool.runQuery("SELECT category,name,value FROM param_gen WHERE profile_id=?", self.profiles[profile])
         d.addCallback(fillParams)
         return d
 
+    def getIndParam(self, category, name, profile):
+        """Ask database for the value of one specific individual parameter
+        @param category: category of the parameter
+        @param name: name of the parameter
+        @param profile: %(doc_profile)s
+        @return: deferred"""
+        d = self.dbpool.runQuery("SELECT value FROM param_ind WHERE category=? AND name=? AND profile_id=?", (category,name,self.profiles[profile]))
+        d.addCallback(self.__getFirstResult)
+        return d
+
+
     def setGenParam(self, category, name, value):
         """Save the general parameters in database
         @param category: category of the parameter
@@ -134,3 +149,10 @@
         d = self.dbpool.runQuery("REPLACE INTO param_ind(category,name,profile_id,value) VALUES (?,?,?,?)", (category, name, self.profiles[profile], value))
         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})))
         return d
+
+    ##Helper methods##
+
+    def __getFirstResult(self, result):
+        """Return the first result of a database query
+        Useful when we are looking for one specific value"""
+        return "" if not result else result[0][0]