Enabling logging to raise exceptions, hardening my exception handling, and tracking...
[theonering] / src / gvoice / addressbook.py
1 #!/usr/bin/python
2
3
4 import logging
5
6 import util.coroutines as coroutines
7 import util.misc as misc_utils
8 import util.go_utils as gobject_utils
9
10
11 _moduleLogger = logging.getLogger(__name__)
12
13
14 class Addressbook(object):
15
16         _RESPONSE_GOOD = 0
17         _RESPONSE_BLOCKED = 3
18
19         def __init__(self, backend, asyncPool):
20                 self._backend = backend
21                 self._numbers = {}
22                 self._asyncPool = asyncPool
23
24                 self.updateSignalHandler = coroutines.CoTee()
25
26         def update(self, force=False):
27                 if not force and self._numbers:
28                         return
29
30                 le = gobject_utils.AsyncLinearExecution(self._asyncPool, self._update)
31                 le.start()
32
33         @misc_utils.log_exception(_moduleLogger)
34         def _update(self):
35                 try:
36                         contacts = yield (
37                                 self._backend.get_contacts,
38                                 (),
39                                 {},
40                         )
41                 except Exception:
42                         _moduleLogger.exception("While updating the addressbook")
43                         return
44
45                 oldContacts = self._numbers
46                 oldContactNumbers = set(self.get_numbers())
47
48                 self._numbers = self._populate_contacts(contacts)
49                 newContactNumbers = set(self.get_numbers())
50
51                 addedContacts = newContactNumbers - oldContactNumbers
52                 removedContacts = oldContactNumbers - newContactNumbers
53                 changedContacts = set(
54                         contactNumber
55                         for contactNumber in newContactNumbers.intersection(oldContactNumbers)
56                         if self._numbers[contactNumber] != oldContacts[contactNumber]
57                 )
58
59                 if addedContacts or removedContacts or changedContacts:
60                         message = self, addedContacts, removedContacts, changedContacts
61                         self.updateSignalHandler.stage.send(message)
62
63         def get_numbers(self):
64                 return self._numbers.iterkeys()
65
66         def get_contact_name(self, strippedNumber):
67                 """
68                 @throws KeyError if contact not in list (so client can choose what to display)
69                 """
70                 return self._numbers[strippedNumber][0]
71
72         def get_phone_type(self, strippedNumber):
73                 try:
74                         return self._numbers[strippedNumber][1]
75                 except KeyError:
76                         return "unknown"
77
78         def is_blocked(self, strippedNumber):
79                 try:
80                         return self._numbers[strippedNumber][2]["response"] == self._RESPONSE_BLOCKED
81                 except KeyError:
82                         return False
83
84         def _populate_contacts(self, contacts):
85                 numbers = {}
86                 for contactId, contactDetails in contacts:
87                         contactName = contactDetails["name"]
88                         contactNumbers = (
89                                 (
90                                         misc_utils.normalize_number(numberDetails["phoneNumber"]),
91                                         numberDetails.get("phoneType", "Mobile"),
92                                 )
93                                 for numberDetails in contactDetails["numbers"]
94                         )
95                         numbers.update(
96                                 (number, (contactName, phoneType, contactDetails))
97                                 for (number, phoneType) in contactNumbers
98                         )
99                 return numbers