Add support for creating contacts from "interesting" friends on LinkedIn. Completes...
[hermes] / package / src / org / maemo / hermes / engine / facebook / service.py
1 import org.maemo.hermes.engine.service
2
3 from org.maemo.hermes.engine.names import canonical
4 from org.maemo.hermes.engine.friend import Friend
5
6 class Service(org.maemo.hermes.engine.service.Service):
7     """Facebook backend for Hermes.
8                 
9        Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
10        Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
11        Released under the Artistic Licence."""
12        
13     attrs = ['uid', 'name', 'pic_big', 'birthday_date', 'profile_url', 'first_name', 'last_name', 'website']
14
15
16     # -----------------------------------------------------------------------
17     def __init__(self, service_id, facebook, create_birthday_only = False):
18         self.fb = facebook
19         self._service_id = service_id
20         self._create_birthday_only = create_birthday_only
21         
22         self._friends_by_name = {}
23         self._friends_by_url = {}
24         self._friends_by_contact = {}
25         self._contacts_by_friend = {}
26         self._friends_without_contact = set()
27         self._known_urls = set()
28
29
30     # -----------------------------------------------------------------------
31     def get_friends_to_create_contacts_for(self):
32         friends = []
33         if self._create_birthday_only:
34             for friend in self._friends_without_contact:
35                 friends.append(friend)
36                     
37         return friends
38     
39     
40     # -----------------------------------------------------------------------
41     def get_friends(self):
42         """Returns all friends on Facebook"""
43         
44         return self._friends_by_url.values()
45     
46     
47     # -----------------------------------------------------------------------
48     def get_contacts_with_match(self):
49         """Returns a dict, where each key value pair is a contact (key) that 
50            matched a friend (value)"""
51
52         return self._friends_by_contact
53     
54     
55     # -----------------------------------------------------------------------
56     def get_unmatched_friends(self):
57         """Returns a list of all friends that didn't match a contact."""
58          
59         return self._friends_without_contact
60
61
62     # -----------------------------------------------------------------------
63     def pre_process_contact(self, contact):
64         """Registers URLs of all previous mappings, and makes sure that any 
65            friend with such a URL don't get match by name."""
66            
67         for url in contact.get_urls():
68             self._known_urls.add(url)
69     
70     
71     # -----------------------------------------------------------------------
72     def process_friends(self):
73         """Retreives data from Facebook and parse that into Friend 
74            objects."""
75         
76         def if_defined(data, key, callback):
77             if key in data and data[key]:
78                 callback(data[key])
79         
80         friends_data = self._get_friends_data()
81         for data in friends_data:
82             friend = Friend(data['name'])
83         
84             if 'profile_url' not in data:
85                 data['profile_url'] = "http://www.facebook.com/profile.php?id=" + str(data['uid'])
86         
87             if_defined(data, 'website', friend.add_url)
88             if_defined(data, 'profile_url', friend.add_url)
89             if_defined(data, 'birthday_date', friend.set_birthday_date)
90
91             if_defined(data, 'pic_big', friend.set_photo_url)
92             
93             url = data['profile_url']
94             friend.add_url(url)
95             self._register_friend(friend)
96
97
98     # -----------------------------------------------------------------------
99     def process_contact(self, contact):
100         """If the contact is matched with a friend, that friend is returned,
101            otherwise None."""
102            
103         matched_friend = None
104         if self._friends_by_contact.has_key(contact):
105             matched_friend = self._friends_by_contact[contact]
106         
107         # we might get a hit if the friend has setup a URL with another service,
108         # such as putting the id link to Facebook on the Twitter account's profile
109         if not matched_friend:
110             for url in contact.get_urls():
111                 if url in self._friends_by_url:
112                     matched_friend = self._friends_by_url[url]
113                     self._register_match(contact, matched_friend)
114                     break
115
116         if not matched_friend:
117             for id in contact.get_identifiers():
118                 if id in self._friends_by_name:
119                     matched_friend = self._friends_by_name.pop(id)
120                     self._register_match(contact, matched_friend)
121                     break
122                 
123         return matched_friend
124     
125
126     # -----------------------------------------------------------------------
127     def _register_friend(self, friend):
128         self._friends_without_contact.add(friend)
129         
130         for url in friend.get_urls():
131             if self.is_profile_url(url):
132                 self._friends_by_url[url] = friend
133
134         if self._allow_friend_to_match_by_name(friend):
135             key = canonical(friend.get_name())
136             self._friends_by_name[key] = friend
137
138
139     # -----------------------------------------------------------------------
140     def is_profile_url(self, url):
141         """Return True if this is a URL for this service."""
142
143         return url and "facebook.com" in url
144
145
146     # -----------------------------------------------------------------------
147     def _allow_friend_to_match_by_name(self, friend):
148         for url in friend.get_urls():
149             if url in self._known_urls:
150                 return False
151         return True
152
153
154     # -----------------------------------------------------------------------
155     def _register_match(self, contact, friend):
156         friend.set_contact(contact)
157         self._friends_without_contact.discard(friend)
158         self._friends_by_contact[contact] = friend
159         self._contacts_by_friend[friend] = contact
160
161
162     # -----------------------------------------------------------------------
163     def _get_friends_data(self):
164         """Returns a list of dicts, where each dict represents the raw data
165            of a friend"""
166         
167         return self.fb.users.getInfo(self.fb.friends.get(), Service.attrs)