Limiting the size of the cache
[theonering] / src / gvoice / addressbook.py
index e796583..2f6c7e2 100644 (file)
@@ -3,36 +3,81 @@
 
 import logging
 
+import util.coroutines as coroutines
+import util.misc as misc_utils
 
-_moduleLogger = logging.getLogger("gvoice.addressbook")
+
+_moduleLogger = logging.getLogger(__name__)
 
 
 class Addressbook(object):
 
+       _RESPONSE_GOOD = 0
+       _RESPONSE_BLOCKED = 3
+
        def __init__(self, backend):
                self._backend = backend
-               self._contacts = {}
+               self._numbers = {}
 
-       def clear_cache(self):
-               self._contacts.clear()
+               self.updateSignalHandler = coroutines.CoTee()
 
-       def get_contacts(self):
-               self._populate_contacts()
-               return self._contacts.iterkeys()
+       def update(self, force=False):
+               if not force and self._numbers:
+                       return
+               oldContacts = self._numbers
+               oldContactNumbers = set(self.get_numbers())
 
-       def get_contact_details(self, contactId):
+               self._numbers = {}
                self._populate_contacts()
-               self._populate_contact_details(contactId)
-               return self._contacts[contactId]
+               newContactNumbers = set(self.get_numbers())
+
+               addedContacts = newContactNumbers - oldContactNumbers
+               removedContacts = oldContactNumbers - newContactNumbers
+               changedContacts = set(
+                       contactNumber
+                       for contactNumber in newContactNumbers.intersection(oldContactNumbers)
+                       if self._numbers[contactNumber] != oldContacts[contactNumber]
+               )
+
+               if addedContacts or removedContacts or changedContacts:
+                       message = self, addedContacts, removedContacts, changedContacts
+                       self.updateSignalHandler.stage.send(message)
+
+       def get_numbers(self):
+               return self._numbers.iterkeys()
+
+       def get_contact_name(self, strippedNumber):
+               """
+               @throws KeyError if contact not in list (so client can choose what to display)
+               """
+               return self._numbers[strippedNumber][0]
+
+       def get_phone_type(self, strippedNumber):
+               try:
+                       return self._numbers[strippedNumber][1]
+               except KeyError:
+                       return "unknown"
+
+       def is_blocked(self, strippedNumber):
+               try:
+                       return self._numbers[strippedNumber][2]["response"] == self._RESPONSE_BLOCKED
+               except KeyError:
+                       return False
 
        def _populate_contacts(self):
-               if self._contacts:
+               if self._numbers:
                        return
                contacts = self._backend.get_contacts()
-               for contactId, contactName in contacts:
-                       self._contacts[contactId] = None
-
-       def _populate_contact_details(self, contactId):
-               if self._contacts[contactId] is not None:
-                       return
-               self._contacts[contactId] = self._backend.get_contact_details(contactId)
+               for contactId, contactDetails in contacts:
+                       contactName = contactDetails["name"]
+                       contactNumbers = (
+                               (
+                                       misc_utils.normalize_number(numberDetails["phoneNumber"]),
+                                       numberDetails.get("phoneType", "Mobile"),
+                               )
+                               for numberDetails in contactDetails["numbers"]
+                       )
+                       self._numbers.update(
+                               (number, (contactName, phoneType, contactDetails))
+                               for (number, phoneType) in contactNumbers
+                       )