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()