Add additional debugging to try and identify cause of MB#11103
[hermes] / package / src / org / maemo / hermes / engine / linkedin / api.py
1 import re
2 import httplib
3 import gnome.gconf
4 from oauth import oauth
5 from xml.dom.minidom import parseString
6 from org.maemo.hermes.engine.phonenumber import PhoneNumber
7 from org.maemo.hermes.engine.friend import Friend
8 from org.maemo.hermes.engine.service import CredentialsExpiredException
9
10 class LinkedInApi():
11     """LinkedIn API for Hermes.
12                 
13        Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
14        Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
15        Released under the Artistic Licence."""
16     
17     LI_API_URL = "https://api.linkedin.com"
18     LI_CONN_API_URL = LI_API_URL + "/v1/people/~/connections:(id,first-name,last-name,picture-url,site-standard-profile-request:(url),date-of-birth,main-address,location:(country:(code)),phone-numbers,member-url-resources)"
19     LI_PROFILE_API_URL = LI_API_URL + "/v1/people/~"
20     
21     
22     # -----------------------------------------------------------------------
23     def __init__(self, consumer, api_request):
24         """Instantiate with an OAuth consumer"""
25         self.consumer = consumer
26         self._make_api_request = api_request
27     
28
29     # -----------------------------------------------------------------------
30     def verify_verifier(self, access_token):
31         try:
32             xml = self._make_api_request(self.LI_PROFILE_API_URL)
33             dom = parseString(xml)
34             friends = self._parse_dom(dom)
35             return friends[0].get_name()
36         except Exception, e:
37             import traceback
38             traceback.print_exc()
39             raise Exception("LinkedIn authorization failed, try again")
40
41     
42     
43     # -----------------------------------------------------------------------
44     def get_friends(self):
45         """ Returns a Friend object for each LinkedIn connection."""
46         
47         xml = self._make_api_request(self.LI_CONN_API_URL)
48         dom = parseString(xml)
49         friends = self._parse_dom(dom)
50
51         return friends
52         
53
54     # -----------------------------------------------------------------------
55     def get_friend_details(self, url, header_value):
56         oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=url)
57         oauth_request.sign_request(self.sig_method, self.consumer, self.access_token)
58
59         headers = oauth_request.to_header()
60         headers[u'x-li-auth-token'] = header_value
61         connection = httplib.HTTPConnection("api.linkedin.com")
62         connection.request(oauth_request.http_method, url, headers=headers)
63         data = connection.getresponse().read()
64         return data
65
66
67     # -----------------------------------------------------------------------
68     def _parse_dom(self, dom):
69         def get_first_tag(node, tagName):
70             if node:
71                 tags = node.getElementsByTagName(tagName)
72                 if tags and len(tags) > 0:
73                     return tags[0]
74         
75         def extract(node, tagName):
76             tag = get_first_tag(node, tagName)
77             if tag:
78                 return tag.firstChild.nodeValue
79             
80         def extract_public_url(node):
81             tag = get_first_tag(node, 'site-standard-profile-request')
82             if tag:
83                 url = extract(tag, 'url').replace("&amp;", "&")
84                 return re.sub('[?&](auth|trk)\w*=[^&]*', '', url)
85             
86         def extract_phone_numbers(node):
87             country = extract(get_first_tag(node, 'country'), 'code')
88             tag = get_first_tag(node, 'phone-numbers')
89             numbers = []
90             if tag:
91                 for element in tag.childNodes:
92                     if element.nodeName != 'phone-number':
93                         continue
94                      
95                     phone_type = extract(element, 'phone-type')
96                     device = phone_type == 'mobile' and phone_type or None
97                     type = phone_type in set(['home', 'work']) and phone_type or None
98                     
99                     number = PhoneNumber(extract(element, 'phone-number'), device = device, type = type, country = country)
100                     numbers.append(number)
101             return numbers
102         
103         def extract_urls(node):
104             tag = get_first_tag(node, 'member-url-resources')
105             urls = []
106             if tag:
107                 for element in tag.getElementsByTagName('url'):
108                     urls.append(element.firstChild.nodeValue.replace("&amp;", "&"))
109             return urls
110         
111         def extract_birthday(node):
112             tag = get_first_tag(node, 'date-of-birth')
113             bday = None
114             if tag:
115                 month = extract(tag, 'month')
116                 day   = extract(tag, 'day')
117                 year  = extract(tag, 'year')
118                 if month and day:
119                     bday = '%s/%s' % (extract(tag, 'month'), extract(tag, 'day'))
120                     if year:
121                         bday = '%s/%s' % (bday, year)
122                     
123             return bday
124         
125         # look for errors
126         errors = dom.getElementsByTagName('error')
127         if errors and len(errors) > 0:
128             details = ""
129             try:
130                 details = " (" + extract(errors[0], "message") + ")"
131             except:
132                 pass
133             if details.startswith('([unauth'):
134                 raise CredentialsExpiredException('linkedin')
135             else:
136                 raise Exception("LinkedIn communication errors detected" + details)
137         
138         friends = []
139         people = dom.getElementsByTagName('person')
140         for p in people:
141             fn = extract(p, 'first-name')
142             ln = extract(p, 'last-name')
143             photo_url = extract(p, 'picture-url')
144             id = extract(p, 'id')
145             public_url = extract_public_url(p)
146             bday = extract_birthday(p)
147
148             name = fn + " " + ln
149             friend = Friend(name)
150             friend.add_url(public_url)
151             if photo_url:
152                 friend.set_photo_url(photo_url)
153                 
154             if bday:
155                 friend.set_birthday_date(bday)
156                 
157             for number in extract_phone_numbers(p):
158                 friend.add_phone(number)
159                 
160             for url in extract_urls(p):
161                 friend.add_url(url)
162             
163             friends.append(friend)
164
165         return friends