From 554299aa91aadaf25f79c3c6dc7b596b108eb347 Mon Sep 17 00:00:00 2001 From: Goffi Date: Tue, 1 Nov 2011 20:39:22 +0100 Subject: [PATCH] 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 --- frontends/src/bridge/DBus.py | 3 + frontends/src/primitivus/profile_manager.py | 40 +++++++---- src/bridge/DBus.py | 6 ++ .../bridge_constructor/bridge_template.ini | 14 ++++ src/core/sat_main.py | 1 + src/tools/memory.py | 72 +++++++++++++++---- src/tools/sqlite.py | 26 ++++++- 7 files changed, 134 insertions(+), 28 deletions(-) diff --git a/frontends/src/bridge/DBus.py b/frontends/src/bridge/DBus.py index 174f1c28..8578934a 100644 --- a/frontends/src/bridge/DBus.py +++ b/frontends/src/bridge/DBus.py @@ -62,6 +62,9 @@ class DBusBridgeFrontend(BridgeFrontend): 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)) diff --git a/frontends/src/primitivus/profile_manager.py b/frontends/src/primitivus/profile_manager.py index 905c0999..f7bf7ea9 100644 --- a/frontends/src/primitivus/profile_manager.py +++ b/frontends/src/primitivus/profile_manager.py @@ -91,12 +91,15 @@ class ProfileManager(urwid.WidgetWrap): 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 @@ class ProfileManager(urwid.WidgetWrap): 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")) diff --git a/src/bridge/DBus.py b/src/bridge/DBus.py index b6df7ef3..e133aa18 100755 --- a/src/bridge/DBus.py +++ b/src/bridge/DBus.py @@ -136,6 +136,12 @@ class DbusObject(dbus.service.Object): def asyncConnect(self, profile_key="@DEFAULT@", callback=None, errback=None): return self.cb["asyncConnect"](unicode(profile_key), callback, errback) + @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) diff --git a/src/bridge/bridge_constructor/bridge_template.ini b/src/bridge/bridge_constructor/bridge_template.ini index ca13a0fa..b5fdb3f8 100644 --- a/src/bridge/bridge_constructor/bridge_template.ini +++ b/src/bridge/bridge_constructor/bridge_template.ini @@ -360,6 +360,20 @@ sig_in=ssss 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] diff --git a/src/core/sat_main.py b/src/core/sat_main.py index 0203c30d..17aeb077 100644 --- a/src/core/sat_main.py +++ b/src/core/sat_main.py @@ -130,6 +130,7 @@ class SAT(service.Service): 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) diff --git a/src/tools/memory.py b/src/tools/memory.py index 9cf6d032..ae32bd70 100644 --- a/src/tools/memory.py +++ b/src/tools/memory.py @@ -39,6 +39,9 @@ SAVEFILE_HISTORY="/history" 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 @@ class Param(): """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 @@ class Param(): 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 @@ class Param(): 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 @@ class Param(): 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 @@ class Param(): 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 @@ class Memory: 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 @@ class Memory: host.set_const('savefile_database', SAVEFILE_DATABASE) database_file = os.path.expanduser(self.getConfig('','local_dir')+ self.host.get_const('savefile_database')) - self.loadFiles() self.storage = SqliteStorage(database_file) + self.params=Param(host, self.storage) + self.loadFiles() self.storage.initialized.addCallback(lambda ignore: self.load(init_defers)) defer.DeferredList(init_defers).chainDeferred(self.initialized) @@ -506,7 +551,7 @@ class Memory: """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 @@ class Memory: 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) diff --git a/src/tools/sqlite.py b/src/tools/sqlite.py index 53e8ef45..a4ff1f8f 100644 --- a/src/tools/sqlite.py +++ b/src/tools/sqlite.py @@ -98,7 +98,9 @@ class SqliteStorage(): @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 @@ class SqliteStorage(): @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 @@ class SqliteStorage(): 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]