Dependency inject service ID, so that it can be stamped on friends and
[hermes] / package / src / org / maemo / hermes / engine / gravatar / service.py
1 import urllib, hashlib, xmlrpclib
2 import org.maemo.hermes.engine.service
3
4 class Service(org.maemo.hermes.engine.service.Service):
5     """Gravatar backend for Hermes.
6        
7        Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
8        Released under the Artistic Licence."""
9        
10     _image_url_base = "http://www.gravatar.com/avatar.php?"
11        
12     # -----------------------------------------------------------------------
13     def __init__(self, service_id, api_email, api_key):
14         """Initialise the Gravatar service.
15         
16         api_email is the email address to use when talking to the backend.
17         api_key is the "secret" key used when talking to the backend
18         """
19         
20         org.maemo.hermes.engine.service.Service.__init__(self, service_id)
21
22         self._api_email  = api_email
23         self._api_key    = api_key
24         if self._api_key is None or self._api_email is None:
25             raise Exception('No Gravatar application keys found. Installation error.')
26          
27         self._address_to_contact = {}
28         self._hash_to_address = {}
29         self._hash_has_gravatar = {}
30         self._empty_cache = True
31         
32         self._friends_by_contact = {}
33         self._contacts_by_friend = {}
34         
35         self._api_url = 'https://secure.gravatar.com/xmlrpc?user=' + hashlib.md5(self._api_email).hexdigest()
36     
37     
38     # -----------------------------------------------------------------------
39     def pre_process_contact(self, contact):
40         """Extracts addresses from the contact."""
41         for address in contact.get_emails():
42             self._address_to_contact[address] = contact
43     
44     
45     # -----------------------------------------------------------------------
46     def process_contact(self, contact):
47         """On first call (with a contact missing a photo), go get data from Gravatar's servers."""
48         
49         if not self._has_photo(contact):
50             for address in contact.get_emails():
51                 hash = self._get_hash_for_address(address)
52                 if (self._hash_has_gravatar.has_key(hash) and self._hash_has_gravatar[hash]):
53                     friend = self._create_friend(contact.get_name())
54                     friend.set_photo_url(self._get_url_for_email_hash(hash))
55                     self._register_match(contact, friend)
56                     return friend
57         
58         return None
59
60
61     # -----------------------------------------------------------------------
62     def process_friends(self):
63         self._lookup_addresses()
64
65     
66     # -----------------------------------------------------------------------
67     def get_friends(self):
68         return self._contacts_by_friend.keys()
69
70     
71     def get_contacts_with_match(self):
72         """Returns a dict with Contact objects as keys and Friend objects as values"""
73         return self._friends_by_contact
74     
75
76     def get_unmatched_friends(self):
77         """Will always return None - Gravatar only reacts on e-mail address input."""
78         return None
79     
80     
81
82
83     # -----------------------------------------------------------------------
84     def _register_match(self, contact, friend):
85         friend.set_contact(contact)
86         self._friends_by_contact[contact] = friend
87         self._contacts_by_friend[friend] = contact
88         
89     # -----------------------------------------------------------------------
90     # FIXME
91     def _has_photo(self, contact):
92         return False
93     
94         
95     # -----------------------------------------------------------------------
96     def _lookup_addresses(self):
97         """Constructs hashes for address_to_contact, makes call to the Gravatar.com service and updates
98         self._hash_has_gravatar"""
99         
100         addresses = self._address_to_contact.keys()
101         if len(addresses) == 0:
102             self._set_hash_information({})
103         else:
104             args = { "apikey" : self._api_key}
105             hashes = self._construct_hashes(addresses)
106             args["hashes"] = list(hashes)
107             url = self._api_url
108             self._set_hash_information(self._get_hash_info_from_server(args, url))
109             
110
111     # -----------------------------------------------------------------------
112     def _get_hash_info_from_server(self, args, url):
113         """Makes the actual XML-RPC call - override this for testing"""
114         
115         service = xmlrpclib.ServerProxy(url)
116         return service.grav.exists(args)
117
118
119     # -----------------------------------------------------------------------
120     def _set_hash_information(self, hash_info):
121         self._hash_has_gravatar = hash_info
122         self._empty_cache = False
123         
124
125     # -----------------------------------------------------------------------
126     def _get_url_for_email_hash(self, hash, default="404", size="128"):
127         """Assembles the URL to a gravatar, based on a hash of an e-mail address"""
128     
129         return self._image_url_base + urllib.urlencode({'gravatar_id':hash, 'd':default, 'size':size})
130     
131     
132     # -----------------------------------------------------------------------
133     def _construct_hashes(self, addresses):
134         """Creates hashes for all addresses specified, returning a set of hashes"""
135     
136         result = set()
137         for address in addresses:
138             hash = self._get_hash_for_address(address)
139             self._hash_to_address[hash] = address
140             result.add(hash)
141     
142         return result
143
144     # -----------------------------------------------------------------------
145     def _get_hash_for_address(self, address):
146         return hashlib.md5(address.lower()).hexdigest()