Mercurial > libervia-backend
comparison tools/memory.py @ 60:9764e027ecc0
SàT: multi-profile parameters, first draft
- new bridge methods getProfilesList and createProfile
- param xml now use two types: general and individual, general are common parameters, individual parameters are linked to profiles
- indidual xml are constructed from param xml and saved values
/!\ params values are not saved yet
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 28 Jan 2010 02:32:27 +1100 |
parents | a5b5fb5fc9fd |
children | 58d49fc19639 |
comparison
equal
deleted
inserted
replaced
59:3e5abe3bbead | 60:9764e027ecc0 |
---|---|
39 ### TODO: add desciption in params | 39 ### TODO: add desciption in params |
40 | 40 |
41 #TODO: mettre Watched dans un plugin | 41 #TODO: mettre Watched dans un plugin |
42 default_xml = u""" | 42 default_xml = u""" |
43 <params> | 43 <params> |
44 <category name="Connection"> | 44 <general> |
45 <param name="JabberID" value="goffi@necton2.int/TestScript" type="string" /> | 45 </general> |
46 <param name="Password" value="toto" type="password" /> | 46 <individual> |
47 <param name="Server" value="necton2.int" type="string" /> | 47 <category name="Connection"> |
48 <param name="NewAccount" value="Register new account" type="button" callback="registerNewAccount"/> | 48 <param name="JabberID" value="goffi@necton2.int/TestScript" type="string" /> |
49 </category> | 49 <param name="Password" value="toto" type="password" /> |
50 <category name="Misc"> | 50 <param name="Server" value="necton2.int" type="string" /> |
51 <param name="Watched" value="test@Jabber.goffi.int" type="string" /> | 51 <param name="NewAccount" value="Register new account" type="button" callback="registerNewAccount"/> |
52 </category> | 52 </category> |
53 <category name="Misc"> | |
54 <param name="Watched" value="test@Jabber.goffi.int" type="string" /> | |
55 </category> | |
56 </individual> | |
53 </params> | 57 </params> |
54 """ | 58 """ |
55 | 59 |
56 def load_default_params(self): | 60 def load_default_params(self): |
57 self.dom = minidom.parseString(Param.default_xml.encode('utf-8')) | 61 self.dom = minidom.parseString(Param.default_xml.encode('utf-8')) |
62 | 66 |
63 def save(self, file): | 67 def save(self, file): |
64 """Save parameters to xml file""" | 68 """Save parameters to xml file""" |
65 with open(file, 'wb') as xml_file: | 69 with open(file, 'wb') as xml_file: |
66 self.dom.writexml(xml_file) | 70 self.dom.writexml(xml_file) |
67 | 71 |
68 def __init__(self, host): | 72 def __init__(self, host): |
69 debug("Parameters init") | 73 debug("Parameters init") |
70 self.host = host | 74 self.host = host |
75 self.default_profile = None | |
76 self.params = {'goffi':{}} #gof: | |
77 self.params_gen = {} | |
71 host.set_const('savefile_param', SAVEFILE_PARAM) | 78 host.set_const('savefile_param', SAVEFILE_PARAM) |
72 host.set_const('savefile_history', SAVEFILE_HISTORY) | |
73 host.set_const('savefile_private', SAVEFILE_PRIVATE) | |
74 host.registerGeneralCB("registerNewAccount", host.registerNewAccountCB) | 79 host.registerGeneralCB("registerNewAccount", host.registerNewAccountCB) |
75 | 80 |
81 def getProfilesList(self): | |
82 return self.params.keys() | |
83 | |
84 def createProfile(self, name, default=False): | |
85 """Create a new profile | |
86 @param name: Name of the profile | |
87 @param default: True if default value""" | |
88 if self.params.has_key(name): | |
89 info ('The profile name already exists') | |
90 return 1 | |
91 self.params[name]={} | |
92 return 0 | |
93 | |
94 def getProfileName(self, profile_key): | |
95 """return profile according to profile_key | |
96 @param profile_key: profile name or key which can be | |
97 @ALL@ for all profiles | |
98 @DEFAULT@ for default profile | |
99 @return: requested profile name or None if it doesn't exist""" | |
100 if profile_key=='@DEFAULT@': | |
101 if not self.params: | |
102 return None | |
103 info('No default profile, returning first one') #TODO: manage real default profile | |
104 return self.params.keys()[0] #FIXME: gof: temporary, must use real default value, and fallback to first one if it doesn't exists | |
105 if not self.params.has_key(profile_key): | |
106 error ('Trying to access an unknown profile') | |
107 return None | |
108 return profile_key | |
109 | |
76 def __get_unique_node(self, parent, tag, name): | 110 def __get_unique_node(self, parent, tag, name): |
77 """return node with given tag, create a new one if the node doesn't exist | 111 """return node with given tag |
78 @param parent: parent of nodes to check (e.g. documentElement) | 112 @param parent: parent of nodes to check (e.g. documentElement) |
79 @param tag: tag to check (e.g. "category") | 113 @param tag: tag to check (e.g. "category") |
80 @param name: name to check (e.g. "JID") | 114 @param name: name to check (e.g. "JID") |
81 @return: node if it exist or None | 115 @return: node if it exist or None |
82 """ | 116 """ |
117 @param category: category of the parameter | 151 @param category: category of the parameter |
118 @param callback: must return a string with the value (use deferred if needed) | 152 @param callback: must return a string with the value (use deferred if needed) |
119 @param errback: must manage the error with args failure, name, category | 153 @param errback: must manage the error with args failure, name, category |
120 """ | 154 """ |
121 #TODO: send signal param update if value changed | 155 #TODO: send signal param update if value changed |
122 node = self.__getParamNode(name, category) | 156 node = self.__getParamNode(name, category, '@ALL@') |
123 if not node: | 157 if not node: |
124 error("Requested param [%s] in category [%s] doesn't exist !" , name, category) | 158 error("Requested param [%s] in category [%s] doesn't exist !" , name, category) |
125 return | 159 return |
126 if node.getAttribute('default_cb') == 'yes': | 160 if node[1].getAttribute('default_cb') == 'yes': |
127 del node.attributes['default_cb'] | 161 del node[1].attributes['default_cb'] |
128 d = defer.maybeDeferred(callback) | 162 d = defer.maybeDeferred(callback) |
129 d.addCallback(self.__default_ok, name, category) | 163 d.addCallback(self.__default_ok, name, category) |
130 d.addErrback(errback or self.__default_ko, name, category) | 164 d.addErrback(errback or self.__default_ko, name, category) |
131 | 165 |
132 def getParamA(self, name, category, attr="value"): | 166 def getParamA(self, name, category, attr="value", profile_key="@DEFAULT@"): |
133 """Helper method to get a specific attribute | 167 """Helper method to get a specific attribute |
134 @param name: name of the parameter | 168 @param name: name of the parameter |
135 @param category: category of the parameter | 169 @param category: category of the parameter |
136 @param attr: name of the attribute (default: "value") | 170 @param attr: name of the attribute (default: "value") |
171 @param profile: owner of the param (@ALL@ for everyone) | |
137 | 172 |
138 @return: attribute""" | 173 @return: attribute""" |
139 node = self.__getParamNode(name, category) | 174 node = self.__getParamNode(name, category) |
140 if not node: | 175 if not node: |
141 error("Requested param [%s] in category [%s] doesn't exist !" , name, category) | 176 error("Requested param [%s] in category [%s] doesn't exist !" , name, category) |
142 return None | 177 return None |
143 return node.getAttribute(attr) | 178 |
144 | 179 if node[0] == 'general': |
145 def getParams(self): | 180 value = self.__getParam(None, category, name, 'general') |
146 """Return the whole params XML""" | 181 return value or node[1].getAttribute(attr) |
147 return self.dom.toxml() | 182 |
148 | 183 assert(node[0] == 'individual') |
149 def getParamsForCategory(self, category): | 184 |
185 profile = self.getProfileName(profile_key) | |
186 if not profile: | |
187 error('Requesting a param for an non-existant profile') | |
188 return None | |
189 | |
190 return self.__getParam(profile, category, name) or node[1].getAttribute(attr) | |
191 | |
192 | |
193 def __getParam(self, profile, category, name, type='individual'): | |
194 """Return the param, or None if it doesn't exist | |
195 @param profile: the profile name (not profile key, i.e. name and not something like @DEFAULT@) | |
196 @param category: param category | |
197 @param name: param name | |
198 """ | |
199 if type == 'general': | |
200 if self.params_gen.has_key((category, name)): | |
201 return self.params_gen[(category, name)] | |
202 return None #This general param has the default value | |
203 assert (type == 'individual') | |
204 if not self.params.has_key(profile) or not self.params[profile].has_key((category, name)): | |
205 return None | |
206 return self.params[profile][(category, name)] | |
207 | |
208 def __constructProfileXml(self, profile): | |
209 """Construct xml for asked profile, filling values when needed | |
210 /!\ as noticed in doc, don't forget to unlink the minidom.Document | |
211 @param profile: profile name (not key !) | |
212 @return: minidom.Document of the profile xml (cf warning above) | |
213 """ | |
214 prof_xml = minidom.parseString('<params/>') | |
215 | |
216 for type_node in self.dom.documentElement.childNodes: | |
217 if type_node.nodeName == 'general' or type_node.nodeName == 'individual': #we use all params, general and individual | |
218 for cat_node in type_node.childNodes: | |
219 if cat_node.nodeName == 'category': | |
220 category = cat_node.getAttribute('name') | |
221 cat_copy = cat_node.cloneNode(True) #we make a copy for the new xml | |
222 params = cat_copy.getElementsByTagName("param") | |
223 for param_node in params: | |
224 name = param_node.getAttribute('name') | |
225 profile_value = self.__getParam(profile, category, name, type_node.nodeName) | |
226 if profile_value: #there is a value for this profile, we must change the default | |
227 param_node.setAttribute('value', profile_value) | |
228 prof_xml.documentElement.appendChild(cat_copy) | |
229 return prof_xml | |
230 | |
231 | |
232 | |
233 def getParams(self, profile_key='@DEFAULT@'): | |
234 """Construct xml for asked profile | |
235 Take params xml as skeleton""" | |
236 profile = self.getProfileName(profile_key) | |
237 if not profile: | |
238 error("Asking params for inexistant profile") | |
239 return "" | |
240 prof_xml = self.__constructProfileXml(profile) | |
241 return_xml = prof_xml.toxml() | |
242 prof_xml.unlink() | |
243 | |
244 return return_xml | |
245 | |
246 def getParamsForCategory(self, category, profile_key='@DEFAULT@'): | |
150 """Return node's xml for selected category""" | 247 """Return node's xml for selected category""" |
151 for node in self.dom.documentElement.childNodes: | 248 #TODO: manage category of general type (without existant profile) |
249 profile = self.getProfileName(profile_key) | |
250 if not profile: | |
251 error("Asking params for inexistant profile") | |
252 return "" | |
253 prof_xml = self.__constructProfileXml(profile) | |
254 | |
255 for node in prof_xml.getElementsByTagName("category"): | |
152 if node.nodeName == "category" and node.getAttribute("name") == category: | 256 if node.nodeName == "category" and node.getAttribute("name") == category: |
153 return node.toxml() | 257 result = node.toxml() |
258 prof_xml.unlink() | |
259 return result | |
260 | |
261 prof_xml.unlink() | |
154 return "<category />" | 262 return "<category />" |
155 | 263 |
156 def __getParamNode(self, name, category): | 264 def __getParamNode(self, name, category, type="@ALL@"): #FIXME: is type useful ? |
157 for node in self.dom.documentElement.childNodes: | 265 """Return a node from the param_xml |
158 if node.nodeName == "category" and node.getAttribute("name") == category: | 266 @param name: name of the node |
159 params = node.getElementsByTagName("param") | 267 @param category: category of the node |
160 for param in params: | 268 @type: keyword for search: |
161 if param.getAttribute("name") == name: | 269 @ALL@ search everywhere |
162 return param | 270 @GENERAL@ only search in general type |
271 @INDIVIDUAL@ only search in individual type | |
272 @return: a tuple with the node type and the the node, or None if not found""" | |
273 | |
274 for type_node in self.dom.documentElement.childNodes: | |
275 if ( ((type == "@ALL@" or type == "@GENERAL@") and type_node.nodeName == 'general') | |
276 or ( (type == "@ALL@" or type == "@INDIVIDUAL@") and type_node.nodeName == 'individual') ): | |
277 for node in type_node.getElementsByTagName('category'): | |
278 if node.getAttribute("name") == category: | |
279 params = node.getElementsByTagName("param") | |
280 for param in params: | |
281 if param.getAttribute("name") == name: | |
282 return (type_node.nodeName, param) | |
163 return None | 283 return None |
164 | 284 |
165 def getParamsCategories(self): | 285 def getParamsCategories(self): |
166 """return the categories availables""" | 286 """return the categories availables""" |
167 categories=[] | 287 categories=[] |
168 for cat in self.dom.getElementsByTagName("category"): | 288 for cat in self.dom.getElementsByTagName("category"): |
169 categories.append(cat.getAttribute("name")) | 289 categories.append(cat.getAttribute("name")) |
170 return categories | 290 return categories |
171 | 291 |
172 def setParam(self, name, value, category): | 292 def setParam(self, name, value, category, profile_key='@DEFAULT@'): |
173 node = self.__getParamNode(name, category) | 293 """Set a parameter, return None if the parameter is not in param xml""" |
294 | |
295 node = self.__getParamNode(name, category, '@ALL@') | |
174 if not node: | 296 if not node: |
297 error('Requesting an unknown parameter (%s/%s)' % (category, name)) | |
298 return | |
299 | |
300 if node[0] == 'general': | |
301 self.params_gen[(category, name)] = value | |
302 return | |
303 | |
304 assert (node[0] == 'individual') | |
305 | |
306 profile = self.getProfileName(profile_key) | |
307 if not profile: | |
308 error('Trying to set parameter for an unknown profile') | |
175 return #TODO: throw an error | 309 return #TODO: throw an error |
310 | |
176 type = node.getAttribute("type") | 311 type = node.getAttribute("type") |
177 if type=="button": | 312 if type=="button": |
178 print "clique",node.toxml() | 313 print "clique",node.toxml() |
179 else: | 314 else: |
180 node.setAttribute("value", value) | 315 self.params[profile][(category, name)] = value |
181 self.host.bridge.paramUpdate(name, value, category) | 316 self.host.bridge.paramUpdate(name, value, category) #TODO: add profile in signal |
182 | 317 |
183 class Memory: | 318 class Memory: |
184 """This class manage all persistent informations""" | 319 """This class manage all persistent informations""" |
185 | 320 |
186 def __init__(self, host): | 321 def __init__(self, host): |
192 self.params=Param(host) | 327 self.params=Param(host) |
193 self.history={} #used to store chat history (key: short jid) | 328 self.history={} #used to store chat history (key: short jid) |
194 self.private={} #used to store private value | 329 self.private={} #used to store private value |
195 self.disco={} #XXX: maybe best in a separate class | 330 self.disco={} #XXX: maybe best in a separate class |
196 self.features={} | 331 self.features={} |
332 host.set_const('savefile_history', SAVEFILE_HISTORY) | |
333 host.set_const('savefile_private', SAVEFILE_PRIVATE) | |
197 self.load() | 334 self.load() |
198 | 335 |
199 def load(self): | 336 def load(self): |
200 """Load parameters and all memory things from file/db""" | 337 """Load parameters and all memory things from file/db""" |
201 param_file = os.path.expanduser(self.host.get_const('local_dir')+ | 338 param_file = os.path.expanduser(self.host.get_const('local_dir')+ |
251 pickle.dump(self.history, history_pickle) | 388 pickle.dump(self.history, history_pickle) |
252 debug("history saved") | 389 debug("history saved") |
253 with open(private_file, 'w') as private_pickle: | 390 with open(private_file, 'w') as private_pickle: |
254 pickle.dump(self.private, private_pickle) | 391 pickle.dump(self.private, private_pickle) |
255 debug("private values saved") | 392 debug("private values saved") |
393 | |
394 def getProfilesList(self): | |
395 return self.params.getProfilesList() | |
396 | |
397 def createProfile(self, name, default=False): | |
398 """Create a new profile | |
399 @param name: Profile name | |
400 @param default: True if default profile (replace previous default) | |
401 """ | |
402 return self.params.createProfile(name, default) | |
256 | 403 |
257 def addToHistory(self, me_jid, from_jid, to_jid, type, message): | 404 def addToHistory(self, me_jid, from_jid, to_jid, type, message): |
258 me_short=me_jid.userhost() | 405 me_short=me_jid.userhost() |
259 from_short=from_jid.userhost() | 406 from_short=from_jid.userhost() |
260 to_short=to_jid.userhost() | 407 to_short=to_jid.userhost() |