X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=src%2Fgvoice%2Faddressbook.py;h=4b3793ba555c7e2d988e744d57ff5f4a85006d0c;hb=b1c44f59a82702e7e673dccbdd8262db5bc967b6;hp=57b935aaf3acceeaf36942020e8652497afadfd8;hpb=e304e44c4ef882e762bb00ba3b3e094ca5df51ac;p=theonering diff --git a/src/gvoice/addressbook.py b/src/gvoice/addressbook.py index 57b935a..4b3793b 100644 --- a/src/gvoice/addressbook.py +++ b/src/gvoice/addressbook.py @@ -1,83 +1,161 @@ #!/usr/bin/python +from __future__ import with_statement + import logging +try: + import cPickle + pickle = cPickle +except ImportError: + import pickle + +import constants import util.coroutines as coroutines -import util.misc as util_misc +import util.misc as misc_utils +import util.go_utils as gobject_utils -_moduleLogger = logging.getLogger("gvoice.addressbook") +_moduleLogger = logging.getLogger(__name__) class Addressbook(object): - def __init__(self, backend): + _RESPONSE_GOOD = 0 + _RESPONSE_BLOCKED = 3 + + OLDEST_COMPATIBLE_FORMAT_VERSION = misc_utils.parse_version("0.8.0") + + def __init__(self, backend, asyncPool): self._backend = backend - self._contacts = {} + self._numbers = {} + self._asyncPool = asyncPool self.updateSignalHandler = coroutines.CoTee() + def load(self, path): + _moduleLogger.debug("Loading cache") + assert not self._numbers + try: + with open(path, "rb") as f: + fileVersion, fileBuild, contacts = pickle.load(f) + except (pickle.PickleError, IOError, EOFError, ValueError, Exception): + _moduleLogger.exception("While loading") + return + + if contacts and misc_utils.compare_versions( + self.OLDEST_COMPATIBLE_FORMAT_VERSION, + misc_utils.parse_version(fileVersion), + ) <= 0: + _moduleLogger.info("Loaded cache") + self._numbers = contacts + self._loadedFromCache = True + else: + _moduleLogger.debug( + "Skipping cache due to version mismatch (%s-%s)" % ( + fileVersion, fileBuild + ) + ) + + def save(self, path): + _moduleLogger.info("Saving cache") + if not self._numbers: + _moduleLogger.info("Odd, no conversations to cache. Did we never load the cache?") + return + + try: + dataToDump = (constants.__version__, constants.__build__, self._numbers) + with open(path, "wb") as f: + pickle.dump(dataToDump, f, pickle.HIGHEST_PROTOCOL) + except (pickle.PickleError, IOError): + _moduleLogger.exception("While saving for %s" % self._name) + _moduleLogger.info("Cache saved") + def update(self, force=False): - if not force and self._contacts: + if not force and self._numbers: + return + + le = gobject_utils.AsyncLinearExecution(self._asyncPool, self._update) + le.start() + + @misc_utils.log_exception(_moduleLogger) + def _update(self): + try: + contacts = yield ( + self._backend.get_contacts, + (), + {}, + ) + except Exception: + _moduleLogger.exception("While updating the addressbook") return - oldContacts = self._contacts - oldContactIds = set(self.get_contacts()) - self._contacts = {} - self._populate_contacts() - newContactIds = set(self.get_contacts()) + oldContacts = self._numbers + oldContactNumbers = set(self.get_numbers()) - addedContacts = newContactIds - oldContactIds - removedContacts = oldContactIds - newContactIds + self._numbers = self._populate_contacts(contacts) + newContactNumbers = set(self.get_numbers()) + + addedContacts = newContactNumbers - oldContactNumbers + removedContacts = oldContactNumbers - newContactNumbers changedContacts = set( - contactId - for contactId in newContactIds.intersection(oldContactIds) - if self._has_contact_changed(contactId, oldContacts) + 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_contacts(self): - return self._contacts.iterkeys() - - def get_contact_name(self, contactId): - return self._contacts[contactId][0] - - def get_contact_details(self, contactId): - return iter(self._contacts[contactId][1]) - - def find_contacts_with_number(self, queryNumber): - strippedQueryNumber = util_misc.strip_number(queryNumber) - for contactId, (contactName, contactDetails) in self.get_contacts(): - for phoneType, number in contactDetails: - if number == strippedQueryNumber: - yield contactId + 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: - return - contacts = self._backend.get_contacts() + def _populate_contacts(self, contacts): + numbers = {} for contactId, contactDetails in contacts: contactName = contactDetails["name"] - contactNumbers = [ + contactNumbers = ( ( + misc_utils.normalize_number(numberDetails["phoneNumber"]), numberDetails.get("phoneType", "Mobile"), - util_misc.strip_number(numberDetails["phoneNumber"]), ) for numberDetails in contactDetails["numbers"] - ] - self._contacts[contactId] = (contactName, contactNumbers) - - def _has_contact_changed(self, contactId, oldContacts): - oldContact = oldContacts[contactId] - oldContactName = oldContact[0] - oldContactDetails = oldContact[1] - if oldContactName != self.get_contact_name(contactId): - return True - if not oldContactDetails: - return False - # if its already in the old cache, purposefully add it into the new cache - return oldContactDetails != self.get_contact_details(contactId) + ) + numbers.update( + (number, (contactName, phoneType, contactDetails)) + for (number, phoneType) in contactNumbers + ) + return numbers + + +def print_addressbook(path): + import pprint + + try: + with open(path, "rb") as f: + fileVersion, fileBuild, contacts = pickle.load(f) + except (pickle.PickleError, IOError, EOFError, ValueError): + _moduleLogger.exception("") + else: + pprint.pprint((fileVersion, fileBuild)) + pprint.pprint(contacts)