1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
import json
import re
import xml.etree.ElementTree as ET
def lower_camel(string):
if not string or '_' not in string:
return string
result = "".join([x.title() for x in string.split('_')])
return result[0].lower() + result[1:]
def format_language(language):
"""
Attempt to format language parameter as 'ww-WW'.
:param string language: language parameter
"""
if not language:
return language
if not re.match('^[a-zA-Z]{2}[_-][a-zA-Z]{2}$', language):
raise TwiMLException('Invalid value for language parameter.')
return language[0:2].lower() + '-' + language[3:5].upper()
class TwiMLException(Exception):
pass
class TwiML(object):
MAP = {
'from_': 'from',
'xml_lang': 'xml:lang',
'interpret_as': 'interpret-as',
'for_': 'for',
'break_': 'break'
}
def __init__(self, **kwargs):
self.name = self.__class__.__name__
self.value = None
self.verbs = []
self.attrs = {}
for k, v in kwargs.items():
if v is not None:
self.attrs[lower_camel(self.MAP.get(k, k))] = v
def __str__(self):
return self.to_xml()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
return False
def to_xml(self, xml_declaration=True):
"""
Return the contents of this verb as an XML string
:param bool xml_declaration: Include the XML declaration. Defaults to True
"""
xml = ET.tostring(self.xml()).decode('utf-8')
return '<?xml version="1.0" encoding="UTF-8"?>{}'.format(xml) if xml_declaration else xml
def append(self, verb):
"""
Add a TwiML doc
:param verb: TwiML Document
:returns: self
"""
self.nest(verb)
return self
def nest(self, verb):
"""
Add a TwiML doc. Unlike `append()`, this returns the created verb.
:param verb: TwiML Document
:returns: the TwiML verb
"""
if not isinstance(verb, TwiML) and not isinstance(verb, str):
raise TwiMLException('Only nesting of TwiML and strings are allowed')
self.verbs.append(verb)
return verb
def xml(self):
el = ET.Element(self.name)
keys = self.attrs.keys()
keys = sorted(keys)
for a in keys:
value = self.attrs[a]
if isinstance(value, bool):
el.set(a, str(value).lower())
else:
el.set(a, str(value))
if self.value:
if isinstance(self.value, dict):
self.value = json.dumps(self.value)
el.text = self.value
last_child = None
for verb in self.verbs:
if isinstance(verb, str):
if last_child is not None:
last_child.tail = verb
else:
el.text = verb
else:
last_child = verb.xml()
el.append(last_child)
return el
def add_child(self, name, value=None, **kwargs):
return self.nest(GenericNode(name, value, **kwargs))
class GenericNode(TwiML):
def __init__(self, name, value, **kwargs):
super(GenericNode, self).__init__(**kwargs)
self.name = name
self.value = value
|