Backend support for partial history update
[gc-dialer] / src / backends / gv_backend.py
index df9e3a6..17bbc90 100644 (file)
@@ -27,34 +27,44 @@ Resources
 
 from __future__ import with_statement
 
+import itertools
 import logging
 
-import gvoice
+from gvoice import gvoice
 
+from util import io as io_utils
 
-_moduleLogger = logging.getLogger("gv_backend")
+
+_moduleLogger = logging.getLogger(__name__)
 
 
 class GVDialer(object):
 
+       MESSAGE_TEXTS = "Text"
+       MESSAGE_VOICEMAILS = "Voicemail"
+       MESSAGE_ALL = "All"
+
+       HISTORY_RECEIVED = "Received"
+       HISTORY_MISSED = "Missed"
+       HISTORY_PLACED = "Placed"
+       HISTORY_ALL = "All"
+
        def __init__(self, cookieFile = None):
                self._gvoice = gvoice.GVoiceBackend(cookieFile)
-
-               self._contacts = None
+               self._texts = []
+               self._voicemails = []
+               self._received = []
+               self._missed = []
+               self._placed = []
 
        def is_quick_login_possible(self):
                """
-               @returns True then is_authed might be enough to login, else full login is required
+               @returns True then refresh_account_info might be enough to login, else full login is required
                """
                return self._gvoice.is_quick_login_possible()
 
-       def is_authed(self, force = False):
-               """
-               Attempts to detect a current session
-               @note Once logged in try not to reauth more than once a minute.
-               @returns If authenticated
-               """
-               return self._gvoice.is_authed(force)
+       def refresh_account_info(self):
+               return self._gvoice.refresh_account_info()
 
        def login(self, username, password):
                """
@@ -64,8 +74,16 @@ class GVDialer(object):
                return self._gvoice.login(username, password)
 
        def logout(self):
+               self._texts = []
+               self._voicemails = []
+               self._received = []
+               self._missed = []
+               self._placed = []
                return self._gvoice.logout()
 
+       def persist(self):
+               return self._gvoice.persist()
+
        def is_dnd(self):
                return self._gvoice.is_dnd()
 
@@ -85,8 +103,8 @@ class GVDialer(object):
                """
                return self._gvoice.cancel(outgoingNumber)
 
-       def send_sms(self, phoneNumber, message):
-               self._gvoice.send_sms(phoneNumber, message)
+       def send_sms(self, phoneNumbers, message):
+               self._gvoice.send_sms(phoneNumbers, message)
 
        def search(self, query):
                """
@@ -98,15 +116,14 @@ class GVDialer(object):
        def get_feed(self, feed):
                return self._gvoice.get_feed(feed)
 
-       def download(self, messageId, adir):
+       def download(self, messageId, targetPath):
                """
                Download a voicemail or recorded call MP3 matching the given ``msg``
                which can either be a ``Message`` instance, or a SHA1 identifier. 
-               Saves files to ``adir`` (defaults to current directory). 
                Message hashes can be found in ``self.voicemail().messages`` for example. 
                Returns location of saved file.
                """
-               return self._gvoice.download(messageId, adir)
+               self._gvoice.download(messageId, targetPath)
 
        def is_valid_syntax(self, number):
                """
@@ -140,47 +157,60 @@ class GVDialer(object):
                """
                return self._gvoice.get_callback_number()
 
-       def get_recent(self):
+       def get_call_history(self, historyType):
                """
                @returns Iterable of (personsName, phoneNumber, exact date, relative date, action)
                """
-               return self._gvoice.get_recent()
-
-       def get_contacts(self):
-               """
-               @returns Iterable of (contact id, contact name)
-               """
-               self._update_contacts_cache()
-               contactsToSort = [
-                       (contactDetails["name"], contactId)
-                       for contactId, contactDetails in self._contacts.iteritems()
-               ]
-               contactsToSort.sort()
-               return (
-                       (contactId, contactName)
-                       for (contactName, contactId) in contactsToSort
-               )
+               history = list(self._get_call_history(historyType))
+               history.sort(key=lambda item: item["time"])
+               return history
 
-       def get_contact_details(self, contactId):
+       def _get_call_history(self, historyType):
                """
-               @returns Iterable of (Phone Type, Phone Number)
+               @returns Iterable of (personsName, phoneNumber, exact date, relative date, action)
                """
-               if self._contacts is None:
-                       self._update_contacts_cache()
-               contactDetails = self._contacts[contactId]
-               return (
-                       (number.get("phoneType", ""), number["phoneNumber"])
-                       for number in contactDetails["numbers"]
-               )
-
-       def get_messages(self):
-               conversations = self._gvoice.get_conversations()
+               if historyType in [self.HISTORY_RECEIVED, self.HISTORY_ALL] or not self._received:
+                       self._received = list(self._gvoice.get_received_calls())
+                       for item in self._received:
+                               item["action"] = self.HISTORY_RECEIVED
+               if historyType in [self.HISTORY_MISSED, self.HISTORY_ALL] or not self._missed:
+                       self._missed = list(self._gvoice.get_missed_calls())
+                       for item in self._missed:
+                               item["action"] = self.HISTORY_MISSED
+               if historyType in [self.HISTORY_PLACED, self.HISTORY_ALL] or not self._placed:
+                       self._placed = list(self._gvoice.get_placed_calls())
+                       for item in self._placed:
+                               item["action"] = self.HISTORY_PLACED
+               received = self._received
+               missed = self._missed
+               placed = self._placed
+               for item in received:
+                       yield item
+               for item in missed:
+                       yield item
+               for item in placed:
+                       yield item
+
+       def get_messages(self, messageType):
+               messages = list(self._get_messages(messageType))
+               messages.sort(key=lambda message: message["time"])
+               return messages
+
+       def _get_messages(self, messageType):
+               if messageType in [self.MESSAGE_VOICEMAILS, self.MESSAGE_ALL] or not self._voicemails:
+                       self._voicemails = list(self._gvoice.get_voicemails())
+               if messageType in [self.MESSAGE_TEXTS, self.MESSAGE_ALL] or not self._texts:
+                       self._texts = list(self._gvoice.get_texts())
+               voicemails = self._voicemails
+               smss = self._texts
+
+               conversations = itertools.chain(voicemails, smss)
                for conversation in conversations:
                        messages = conversation.messages
-                       messageParts = (
+                       messageParts = [
                                (message.whoFrom, self._format_message(message), message.when)
                                for message in messages
-                       )
+                       ]
 
                        messageDetails = {
                                "id": conversation.id,
@@ -220,9 +250,6 @@ class GVDialer(object):
        def factory_name():
                return "Google Voice"
 
-       def _update_contacts_cache(self):
-               self._contacts = dict(self._gvoice.get_contacts())
-
        def _format_message(self, message):
                messagePartFormat = {
                        "med1": "<i>%s</i>",
@@ -230,7 +257,7 @@ class GVDialer(object):
                        "high": "<b>%s</b>",
                }
                return " ".join(
-                       messagePartFormat[text.accuracy] % text.text
+                       messagePartFormat[text.accuracy] % io_utils.escape(text.text)
                        for text in message.body
                )