Package speakeasy :: Module speakeasy_persistence
[hide private]
[frames] | no frames]

Source Code for Module speakeasy.speakeasy_persistence

  1  from xml.etree import ElementTree 
  2  import os 
  3  import rospy 
  4  import sys 
5 6 # ------------------------------------------------ Support Function -------------------------- 7 8 #---------------------------------- 9 # openTag 10 #-------------- 11 12 -def openTag(tagName):
13 return "<" + tagName + ">"
14
15 #---------------------------------- 16 # closeTag 17 #-------------- 18 19 -def closeTag(tagName):
20 return "</" + tagName + ">"
21
22 #---------------------------------- 23 # indent 24 #-------------- 25 26 -def indent(elem, level=0):
27 i = "\n" + level*" " 28 if len(elem): 29 if not elem.text or not elem.text.strip(): 30 elem.text = i + " " 31 if not elem.tail or not elem.tail.strip(): 32 elem.tail = i 33 for elem in elem: 34 indent(elem, level+1) 35 if not elem.tail or not elem.tail.strip(): 36 elem.tail = i 37 else: 38 if level and (not elem.tail or not elem.tail.strip()): 39 elem.tail = i
40
41 # ------------------------------------------------ Class ElementreeIterator -------------------------- 42 43 -class Python2_7ElementTree(object):
44 ''' 45 This facility is built in to Python 2.7's ElementTree as 46 element.iter(). Also built in with Python 2.7 is findall(tagName). 47 We need to use 2.6 for now, so we recreate the facilities 48 49 ''' 50
51 - def __init__(self, python2_6ElementTree):
52 ''' 53 Pass in an element tree of the old kind. 54 @param python2_6ElementTree: ElementTree instance 55 @type python2_6ElementTree: ElementTree 56 ''' 57 self.elTree = python2_6ElementTree; 58 self.iterIndx = None;
59
60 - def iter(self):
61 ''' 62 Return an object that supports methods next(), hasNext(), and closeIter(). 63 ''' 64 if self.iterIndx is not None: 65 raise ValueError("Can only have one iterator going at a time. Call method closeIter() first."); 66 self.flatNodeList = self.elTree.getiterator(); 67 self.iterIndx = -1; 68 return self;
69
70 - def next(self):
71 ''' 72 Return next subelement in xml tree. 73 @raise ValueError: if no more elements are available. 74 ''' 75 if self.iterIndx is None: 76 raise ValueError("Iterator must first be started with call to method iter()"); 77 self.iterIndx += 1; 78 return self.flatNodeList[self.iterIndx];
79
80 - def hasNext(self):
81 ''' 82 Return True if at least one element has still not been 83 retrieved via the next() method. 84 ''' 85 if self.iterIndx is None: 86 raise ValueError("Iterator must first be started with call to method iter()"); 87 return self.iterIndx < len(self.flatNodeList);
88
89 - def closeIter(self):
90 ''' 91 Indicate that the iterator is no longer needed. 92 Only after calling this method can a new iterator 93 be obtained via iter(). 94 ''' 95 self.iterIndx = None;
96
97 98 # ------------------------------------------------ Class ButtonSavior -------------------------- 99 100 -class ButtonSavior(object):
101 102 BUTTON_PROGRAM_SET_TAG = "buttonProgramSet" 103 BUTTON_PROGRAM_SET_TITLE_TAG = "buttonProgramSetTitle" 104 BUTTON_PROGRAM_TAG = "buttonSetting"; 105 BUTTON_LABEL_TAG = "label"; 106 BUTTON_TEXT_TO_SAY_TAG = "text"; 107 BUTTON_VOICE_TAG = "voice"; 108 BUTTON_TTS_ENGINE = "engine"; 109 BUTTON_PLAY_ONCE = "playOnce"; 110 111 112 SPEECH_SET_DIR = os.path.join(os.path.dirname(__file__),'../..','buttonPrograms'); 113 114 #---------------------------------- 115 # initializer 116 #-------------- 117
118 - def __init__(self):
119 pass;
120 121 #---------------------------------- 122 # saveToFile 123 #-------------- 124 125 @staticmethod
126 - def saveToFile(buttonSettings, fileName, title=None):
127 ''' 128 Given an array of ButtonProgram objects, turn them to 129 XML and save them in a new file of the given name. 130 If the file exists, it will be overwritten. 131 @param buttonSettings: Array of ButtonProgram instances 132 @type buttonSettings: [ButtonProgram] 133 @param fileName: Name of file to which the XML is to be written. 134 @type fileName: string 135 @param title: An arbitrary name for this set 136 @type title: string 137 ''' 138 try: 139 fd = open(fileName, 'w'); 140 except Exception: 141 ButtonSavior.reportError("Cannot open file '" + str(fileName) + "' for saving buttons."); 142 sys.exit(-1); 143 144 if title is None: 145 title = ButtonSavior.findDefaultButtonSetTitle(); 146 147 domRoot = ElementTree.XML(openTag(ButtonSavior.BUTTON_PROGRAM_SET_TAG) +\ 148 closeTag(ButtonSavior.BUTTON_PROGRAM_SET_TAG)); 149 domTitle = ElementTree.XML(openTag(ButtonSavior.BUTTON_PROGRAM_SET_TITLE_TAG) +\ 150 title +\ 151 closeTag(ButtonSavior.BUTTON_PROGRAM_SET_TITLE_TAG)); 152 domRoot.append(domTitle); 153 154 for buttonSetting in buttonSettings: 155 domOneButtonSetting = buttonSetting.toXML(); 156 domRoot.append(domOneButtonSetting); 157 158 indent(domRoot); 159 domTree = ElementTree.ElementTree(domRoot); 160 domTree.write(fd);
161 162 @staticmethod
163 - def retrieveFromFile(fileName, buttonProgramClass):
164 ''' 165 Given an absolute path to a button program XML file, 166 return an iterator over the DOM tree program elements. 167 The caller passes in a class object. This class will 168 be instantiated with the data extracted from the XML 169 of each button program. 170 171 Example structure:: 172 <buttonProgramSet> 173 <buttonProgramSetTitle>Household Set</buttonProgramSetTitle> 174 <buttonSetting> 175 <label>Button 4</label> 176 <text>Utterance number 1</text> 177 <voice>David</voice> 178 <engine>cepstral</engine> 179 <playOnce>True</playOnce> 180 </buttonSetting> 181 <buttonSetting> 182 <label>Button 5</label> 183 <text>Utterance number 2</text> 184 <voice>David</voice> 185 <engine>cepstral</engine> 186 <playOnce>True</playOnce> 187 </buttonSetting> 188 </buttonProgramSet> 189 190 @param fileName: name of XML file. 191 @type fileName: string 192 @param buttonProgramClass: class whose instances are to be created to hold button settings. 193 @type buttonProgramClass: ButtonProgram 194 @return: A tuple containing the title of the button set, and an array of instances of 195 the passed-in class. 196 @rtype: (string, [ButtonProgram]) 197 @raise ValueError: if XML is bad. 198 ''' 199 try: 200 absFileName = os.path.join(ButtonSavior.SPEECH_SET_DIR, fileName); 201 fd = open(absFileName, 'r'); 202 except Exception: 203 ButtonSavior.reportError("Cannot open file '" + str(absFileName) + "' for saving button settings set."); 204 return []; 205 try: 206 domTree = ElementTree.parse(fd); 207 except Exception: 208 ButtonSavior.reportError("Cannot open file '" + str(absFileName) + "' for retrieving button settings set."); 209 return []; 210 211 #domTreeIt = domTree.iter(); # Pythyon 2.7 212 domTreeIt = Python2_7ElementTree(domTree).iter(); 213 buttonProgramObjects = []; 214 215 try: 216 try: 217 # Get the root (outermost) element 'buttonProgramSet': 218 domButtonSetRootEl = domTreeIt.next(); 219 domButtonSetTitleEl = domTreeIt.next(); 220 programSetTitle = domButtonSetTitleEl.text; 221 222 # Get all 'buttonSetting' elements as ElementObject instances: 223 domButtonSettingElArr = domButtonSetRootEl.findall("buttonSetting") 224 225 226 for domButtonSettingEl in domButtonSettingElArr: 227 domButtonLabelEl = domButtonSettingEl.find("label"); 228 label = domButtonLabelEl.text; 229 230 domTextEl = domButtonSettingEl.find("text"); 231 text = domTextEl.text; 232 233 domVoiceEl = domButtonSettingEl.find("voice"); 234 voice = domVoiceEl.text; 235 236 domEngineEl = domButtonSettingEl.find("engine"); 237 engine = domEngineEl.text; 238 239 domPlayOnceEl = domButtonSettingEl.find("playOnce"); 240 playOnce = domPlayOnceEl.text; 241 if playOnce == "True": 242 playOnce = True; 243 else: 244 playOnce = False; 245 246 if text is None: 247 text = ""; 248 buttonProgram = buttonProgramClass(label, text, voice, engine, playOnce); 249 buttonProgramObjects.append(buttonProgram); 250 except StopIteration: 251 pass; 252 except Exception: 253 # Bad XML: 254 raise ValueError("Bad XML in file " + fileName); 255 256 return (programSetTitle, buttonProgramObjects);
257 258 #---------------------------------- 259 # findDefaultButtonSetTitle 260 #-------------- 261 262 @staticmethod
264 return "Default Title" #TODO
265 266 #---------------------------------- 267 # createXMLElement 268 #-------------- 269 270 @staticmethod
271 - def createXMLElement(tagName, content="", attrDict={}):
272 domEl = ElementTree.XML(openTag(tagName) + content + closeTag(tagName)); 273 for attrName in attrDict.keys(): 274 domEl.set(attrName, attrDict[attrName]); 275 return domEl;
276 277 278 #---------------------------------- 279 # reportError 280 #-------------- 281 282 @staticmethod
283 - def reportError(msg):
284 rospy.logerr(msg);
285 #print msg; 286 287 288 # --------------------------------------------------------- Testing -------------------------------- 289 290 if __name__ == "__main__": 291 from speakeasy_controller import ButtonProgram; 292 import tempfile; 293 294 buttonProgs = [ 295 ButtonProgram("Test Label 1", "Test text 1", "David", "Festival"), 296 ButtonProgram("Test Label 2", "Test text 2", "Maria", "Cepstral"), 297 ] 298 for buttonProg in buttonProgs: 299 print str(buttonProg); 300 tmpFile = tempfile.NamedTemporaryFile(delete=False); 301 buttonSavior = ButtonSavior(); 302 buttonSavior.saveToFile(buttonProgs, tmpFile.name, "Title1"); 303 (title, progs) = buttonSavior.retrieveFromFile(tmpFile.name, ButtonProgram); 304 tmpFile.close(); 305 os.remove(tmpFile.name) 306 307 buttonProg = progs[0]; 308 309 if (buttonProg.getLabel() != "Test Label 1") or\ 310 (buttonProg.getText() != "Test text 1") or\ 311 (buttonProg.getVoice() != "David") or\ 312 (buttonProg.getTtsEngine() != "Festival"): 313 raise ValueError("Button program 1 is incorrect: " + 314 "Label is '%s', should be '%s'. " % (buttonProg.getLabel(), "Test Label 1") + 315 "Text is '%s', should be '%s'. " % (buttonProg.getText(), "Test text 1") + 316 "Voice is '%s', should be '%s'. " % (buttonProg.getVoice(), "David") + 317 "TtsEngine is '%s', should be '%s'. " % (buttonProg.getTtsEngine(), "Festival")) 318 319 buttonProg = progs[1]; 320 if buttonProg.getLabel() != "Test Label 2" or buttonProg.getText() != "Test text 2" or buttonProg.getVoice() != "Maria" or buttonProg.getTtsEngine() != "Cepstral": 321 raise ValueError("Button program 2 is incorrect."); 322 323 print "SpeakEasy button program save/restore test successful." 324