util.py
Go to the documentation of this file.
00001 import re
00002 from sys import stdout
00003 from xml.parsers.expat import ParserCreate
00004 from time import gmtime
00005 from datetime import datetime
00006 from pprint import pprint
00007 try:
00008     from urllib2 import build_opener,install_opener, \
00009         HTTPCookieProcessor,Request,urlopen
00010     from urllib import urlencode,quote
00011 except ImportError:
00012     from urllib.request import build_opener,install_opener, \
00013         HTTPCookieProcessor,Request,urlopen
00014     from urllib.parse import urlencode,quote
00015 try:
00016     from http.cookiejar import LWPCookieJar as CookieJar
00017 except ImportError:
00018     from cookielib import LWPCookieJar as CookieJar
00019 try:
00020     from json import loads
00021 except ImportError:
00022     from simplejson import loads
00023 try:
00024     input = raw_input
00025 except NameError:
00026     input = input
00027 
00028 sha1_re = re.compile(r'^[a-fA-F0-9]{40}$')
00029 
00030 def print_(*values, **kwargs):
00031     """
00032     Implementation of Python3's print function
00033     
00034     Prints the values to a stream, or to sys.stdout by default.
00035     Optional keyword arguments:
00036     
00037     file: a file-like object (stream); defaults to the current sys.stdout.
00038     sep:  string inserted between values, default a space.
00039     end:  string appended after the last value, default a newline.
00040     """
00041     fo = kwargs.pop('file', stdout)
00042     fo.write(kwargs.pop('sep', ' ').join(map(str, values)))
00043     fo.write(kwargs.pop('end', '\n'))
00044     fo.flush()
00045 
00046 def is_sha1(s):
00047     """
00048     Returns ``True`` if the string is a SHA1 hash
00049     """
00050     return bool(sha1_re.match(s))
00051 
00052 def validate_response(response):
00053     """
00054     Validates that the JSON response is A-OK
00055     """
00056     try:
00057         assert 'ok' in response and response['ok']
00058     except AssertionError:
00059         raise ValidationError('There was a problem with GV: %s' % response)
00060 
00061 def load_and_validate(response):
00062     """
00063     Loads JSON data from http response then validates
00064     """
00065     validate_response(loads(response.read()))
00066 
00067 class ValidationError(Exception):
00068     """
00069     Bombs when response code back from Voice 500s
00070     """
00071 
00072 class LoginError(Exception):
00073     """
00074     Occurs when login credentials are incorrect
00075     """
00076     
00077 class ParsingError(Exception):
00078     """
00079     Happens when XML feed parsing fails
00080     """
00081     
00082 class JSONError(Exception):
00083     """
00084     Failed JSON deserialization
00085     """
00086     
00087 class DownloadError(Exception):
00088     """
00089     Cannot download message, probably not in voicemail/recorded
00090     """
00091     
00092 class ForwardingError(Exception):
00093     """
00094     Forwarding number given was incorrect
00095     """
00096     
00097     
00098 class AttrDict(dict):
00099     def __getattr__(self, attr):
00100         if attr in self:
00101             return self[attr]
00102 
00103 class Phone(AttrDict):
00104     """
00105     Wrapper for phone objects used for phone specific methods
00106     Attributes are:
00107     
00108      * id: int
00109      * phoneNumber: i18n phone number
00110      * formattedNumber: humanized phone number string
00111      * we: data dict
00112      * wd: data dict
00113      * verified: bool
00114      * name: strign label
00115      * smsEnabled: bool
00116      * scheduleSet: bool
00117      * policyBitmask: int
00118      * weekdayTimes: list
00119      * dEPRECATEDDisabled: bool
00120      * weekdayAllDay: bool
00121      * telephonyVerified
00122      * weekendTimes: list
00123      * active: bool
00124      * weekendAllDay: bool
00125      * enabledForOthers: bool
00126      * type: int (1 - Home, 2 - Mobile, 3 - Work, 4 - Gizmo)
00127             
00128     """
00129     def __init__(self, voice, data):
00130         self.voice = voice
00131         super(Phone, self).__init__(data)
00132     
00133     def enable(self,):
00134         """
00135         Enables this phone for usage
00136         """
00137         return self.__call_forwarding()
00138 
00139     def disable(self):
00140         """
00141         Disables this phone
00142         """
00143         return self.__call_forwarding('0')
00144         
00145     def __call_forwarding(self, enabled='1'):
00146         """
00147         Enables or disables this phone
00148         """
00149         self.voice.__validate_special_page('default_forward',
00150             {'enabled':enabled, 'phoneId': self.id})
00151         
00152     def __str__(self):
00153         return self.phoneNumber
00154     
00155     def __repr__(self):
00156         return '<Phone %s>' % self.phoneNumber
00157         
00158 class Message(AttrDict):
00159     """
00160     Wrapper for all call/sms message instances stored in Google Voice
00161     Attributes are:
00162     
00163      * id: SHA1 identifier
00164      * isTrash: bool
00165      * displayStartDateTime: datetime
00166      * star: bool
00167      * isSpam: bool
00168      * startTime: gmtime
00169      * labels: list
00170      * displayStartTime: time
00171      * children: str
00172      * note: str
00173      * isRead: bool
00174      * displayNumber: str
00175      * relativeStartTime: str
00176      * phoneNumber: str
00177      * type: int
00178      
00179     """
00180     def __init__(self, folder, id, data):
00181         assert is_sha1(id), 'Message id not a SHA1 hash'
00182         self.folder = folder
00183         self.id = id
00184         super(AttrDict, self).__init__(data)
00185         self['startTime'] = gmtime(int(self['startTime'])/1000)
00186         self['displayStartDateTime'] = datetime.strptime(
00187                 self['displayStartDateTime'], '%m/%d/%y %I:%M %p')
00188         self['displayStartTime'] = self['displayStartDateTime'].time()
00189     
00190     def delete(self, trash=1):
00191         """
00192         Moves this message to the Trash. Use ``message.delete(0)`` to move it out of the Trash.
00193         """
00194         self.folder.voice.__messages_post('delete', self.id, trash=trash)
00195 
00196     def star(self, star=1):
00197         """
00198         Star this message. Use ``message.star(0)`` to unstar it.
00199         """
00200         self.folder.voice.__messages_post('star', self.id, star=star)
00201         
00202     def mark(self, read=1):
00203         """
00204         Mark this message as read. Use ``message.mark(0)`` to mark it as unread.
00205         """
00206         self.folder.voice.__messages_post('mark', self.id, read=read)
00207         
00208     def download(self, adir=None):
00209         """
00210         Download the message MP3 (if any). 
00211         Saves files to ``adir`` (defaults to current directory). 
00212         Message hashes can be found in ``self.voicemail().messages`` for example. 
00213         Returns location of saved file.        
00214         """
00215         return self.folder.voice.download(self, adir)
00216 
00217     def __str__(self):
00218         return self.id
00219     
00220     def __repr__(self):
00221         return '<Message #%s (%s)>' % (self.id, self.phoneNumber)
00222 
00223 class Folder(AttrDict):
00224     """
00225     Folder wrapper for feeds from Google Voice
00226     Attributes are:
00227     
00228      * totalSize: int (aka ``__len__``)
00229      * unreadCounts: dict
00230      * resultsPerPage: int
00231      * messages: list of Message instances
00232     """
00233     def __init__(self, voice, name, data):
00234         self.voice = voice
00235         self.name = name
00236         super(AttrDict, self).__init__(data)
00237         
00238     def messages(self):
00239         """
00240         Returns a list of all messages in this folder
00241         """
00242         return [Message(self, *i) for i in self['messages'].items()]
00243     messages = property(messages)
00244     
00245     def __len__(self):
00246         return self['totalSize']
00247 
00248     def __repr__(self):
00249         return '<Folder %s (%s)>' % (self.name, len(self))
00250     
00251 class XMLParser(object):
00252     """
00253     XML Parser helper that can dig json and html out of the feeds. 
00254     The parser takes a ``Voice`` instance, page name, and function to grab data from. 
00255     Calling the parser calls the data function once, sets up the ``json`` and ``html``
00256     attributes and returns a ``Folder`` instance for the given page::
00257     
00258         >>> o = XMLParser(voice, 'voicemail', lambda: 'some xml payload')
00259         >>> o()
00260         ... <Folder ...>
00261         >>> o.json
00262         ... 'some json payload'
00263         >>> o.data
00264         ... 'loaded json payload'
00265         >>> o.html
00266         ... 'some html payload'
00267         
00268     """
00269     attr = None
00270         
00271     def start_element(self, name, attrs):
00272         if name in ('json','html'):
00273             self.attr = name
00274     def end_element(self, name): self.attr = None
00275     def char_data(self, data):
00276         if self.attr and data:
00277             setattr(self, self.attr, getattr(self, self.attr) + data)
00278 
00279     def __init__(self, voice, name, datafunc):
00280         self.json, self.html = '',''
00281         self.datafunc = datafunc
00282         self.voice = voice
00283         self.name = name
00284         
00285     def __call__(self):
00286         self.json, self.html = '',''
00287         parser = ParserCreate()
00288         parser.StartElementHandler = self.start_element
00289         parser.EndElementHandler = self.end_element
00290         parser.CharacterDataHandler = self.char_data
00291         try:
00292             data = self.datafunc()
00293             parser.Parse(data, 1)
00294         except:
00295             raise ParsingError
00296         return self.folder
00297 
00298     def folder(self):
00299         """
00300         Returns associated ``Folder`` instance for given page (``self.name``)
00301         """
00302         return Folder(self.voice, self.name, self.data)        
00303     folder = property(folder)
00304     
00305     def data(self):
00306         """
00307         Returns the parsed json information after calling the XMLParser
00308         """
00309         try:
00310             return loads(self.json)
00311         except:
00312             raise JSONError
00313     data = property(data)
00314     


continuous_ops_alerts
Author(s): Eitan Marder-Eppstein
autogenerated on Fri Dec 6 2013 19:59:13