Add phone number support to friends, and contacts, and populate from
authorAndrew Flegg <andrew@bleb.org>
Sat, 3 Jul 2010 19:34:23 +0000 (20:34 +0100)
committerAndrew Flegg <andrew@bleb.org>
Sat, 3 Jul 2010 19:34:23 +0000 (20:34 +0100)
LinkedIn (MB#10801)

package/src/org/maemo/hermes/engine/contact.py
package/src/org/maemo/hermes/engine/friend.py
package/src/org/maemo/hermes/engine/linkedin/api.py
package/src/org/maemo/hermes/engine/phonenumber.py [new file with mode: 0644]
package/src/pygobject.py
package/test/automatic_tests.py
package/test/unit/test_friend.py [new file with mode: 0644]
package/test/unit/test_phonenumber.py [new file with mode: 0644]

index 9953048..a309b29 100644 (file)
@@ -5,6 +5,7 @@ import StringIO
 import datetime
 import re
 from org.maemo.hermes.engine.names import canonical, variants
+from org.maemo.hermes.engine.phonenumber import PhoneNumber
 from pygobject import *
 from ctypes import *
 
@@ -14,6 +15,7 @@ E_CONTACT_HOMEPAGE_URL = 42
 E_CONTACT_PHOTO = 94
 E_CONTACT_EMAIL = 97
 E_CONTACT_BIRTHDAY_DATE = 107
+E_CONTACT_PHONE_OTHER = 30
 
 
 class Contact:
@@ -206,7 +208,6 @@ class Contact:
         return emails
         
       
-      
     # -----------------------------------------------------------------------
     def get_urls(self):
         """Return a list of URLs which are associated with this contact."""
@@ -220,6 +221,32 @@ class Contact:
             urls.append(string_at(attr.value().next()))
           
         return urls
+        
+      
+    # -----------------------------------------------------------------------
+    def get_phones(self):
+        """Return a list of phone numbers associated with this contact."""
+        
+        nums = []
+        ai = GList.new(ebook.e_contact_get_attributes(hash(self._contact), E_CONTACT_PHONE_OTHER))
+        while ai.has_next():
+            attr = ai.next(as_a = EVCardAttribute)
+            types = set()
+            if attr.params:
+                params = GList.new(ebook.e_vcard_attribute_param_get_values(attr.params.contents.next()))
+                while params.has_next():
+                    types.add(string_at(params.next()))
+                
+            device = 'VOICE' in types and 'landline' \
+                  or 'CELL'  in types and 'mobile'   \
+                  or None
+            type = 'HOME' in types and 'home' \
+                or 'WORK' in types and 'work' \
+                or None
+            number = string_at(attr.value().next())
+            nums.append(PhoneNumber(number, type = type, device = device))
+          
+        return nums
     
       
     # -----------------------------------------------------------------------
@@ -233,6 +260,45 @@ class Contact:
         
         return updated
     
+      
+    # -----------------------------------------------------------------------
+    def add_phone(self, phone):
+        """Add a new phone number to the set of numbers for this contact."""
+        
+        compare = phone.get_number()[-6:]
+        for test in self.get_phones():
+            if test.get_number().endswith(compare):
+                return False
+            
+        ai = GList.new(ebook.e_contact_get_attributes(hash(self._contact), E_CONTACT_PHONE_OTHER))
+        ai.add()
+        tel_attr = EVCardAttribute()
+        tel_attr.group = ''
+        tel_attr.name = 'TEL'
+        
+        if phone.get_device() or phone.get_type():
+            param = ebook.e_vcard_attribute_param_new('TYPE')
+            if phone.get_device():
+                ebook.e_vcard_attribute_param_add_value(param, 
+                          create_string_buffer(phone.get_device() == 'landline' and 'VOICE' \
+                                            or phone.get_device() == 'mobile' and 'CELL' \
+                                            or ''))
+            if phone.get_type():
+                ebook.e_vcard_attribute_param_add_value(param, 
+                          create_string_buffer(phone.get_type().upper()))
+            
+            params = GList()
+            params.set(param)
+            tel_attr.params = cast(addressof(params), POINTER(GList))
+            
+        val = GList()
+        val.set(create_string_buffer(phone.get_number()))
+        ai.set(addressof(tel_attr))
+        tel_attr.values = cast(addressof(val), POINTER(GList))
+        ebook.e_contact_set_attributes(hash(self._contact), E_CONTACT_PHONE_OTHER, addressof(ai))
+        return True
+        
+    
     
     # -----------------------------------------------------------------------
     def _add_url(self, url, unique):
index c1347be..687f785 100644 (file)
@@ -28,6 +28,9 @@ class Friend():
     
     def add_url(self, url):
         self._add('url', url)
+    
+    def add_phone(self, phone):
+        self._add('phone', phone)
         
     def get_birthday_date(self):
         return self._safe_get('bday')
@@ -50,6 +53,10 @@ class Friend():
     def get_urls(self):
         try: return self._multi_attributes['url'] 
         except: return []
+    
+    def get_phones(self):
+        try: return self._multi_attributes['phone'] 
+        except: return []
         
     def get_photo_url(self):
         return self._safe_get('photo-url')
@@ -110,6 +117,10 @@ class Friend():
         if self._multi_attributes.has_key('url'):
             for url in self._multi_attributes['url']:
                 updated += contact.add_url(url)
+
+        if self._multi_attributes.has_key('phone'):
+            for url in self._multi_attributes['phone']:
+                updated += contact.add_phone(url)
                 
         return updated
 
index 882360a..6f87b2c 100644 (file)
@@ -3,12 +3,14 @@ import httplib
 import gnome.gconf
 from oauth import oauth
 from xml.dom.minidom import parseString
+from org.maemo.hermes.engine.phonenumber import PhoneNumber
 from org.maemo.hermes.engine.friend import Friend
 
 class LinkedInApi():
     """LinkedIn API for Hermes.
                 
        Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
+       Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
        Released under the Artistic Licence."""
        
     GCONF_API_KEY = '/apps/maemo/hermes/linkedin_key'
@@ -18,7 +20,7 @@ class LinkedInApi():
     
     LI_SERVER = "api.linkedin.com"
     LI_API_URL = "https://api.linkedin.com"
-    LI_CONN_API_URL = LI_API_URL + "/v1/people/~/connections"
+    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)"
     LI_PROFILE_API_URL = LI_API_URL + "/v1/people/~"
 
     REQUEST_TOKEN_URL = LI_API_URL + "/uas/oauth/requestToken"
@@ -96,9 +98,10 @@ class LinkedInApi():
     # -----------------------------------------------------------------------
     def _parse_dom(self, dom):
         def get_first_tag(node, tagName):
-            tags = node.getElementsByTagName(tagName)
-            if tags and len(tags) > 0:
-                return tags[0]
+            if node:
+                tags = node.getElementsByTagName(tagName)
+                if tags and len(tags) > 0:
+                    return tags[0]
         
         def extract(node, tagName):
             tag = get_first_tag(node, tagName)
@@ -110,6 +113,22 @@ class LinkedInApi():
             if tag:
                 url = extract(tag, 'url').replace("&amp;", "&")
                 return re.sub('[?&](auth|trk)\w*=[^&]*', '', url)
+            
+        def extract_phone_numbers(node):
+            country = extract(get_first_tag(node, 'country'), 'code')
+            tag = get_first_tag(node, 'phone-numbers')
+            numbers = []
+            for element in tag.childNodes:
+                if element.nodeName != 'phone-number':
+                    continue
+                 
+                phone_type = extract(element, 'phone-type')
+                device = phone_type == 'mobile' and phone_type or None
+                type = phone_type in set(['home', 'work']) and phone_type or None
+                
+                number = PhoneNumber(extract(element, 'phone-number'), device = device, type = type, country = country)
+                numbers.append(number)
+            return numbers
         
         # look for errors
         errors = dom.getElementsByTagName('error')
@@ -136,6 +155,9 @@ class LinkedInApi():
                 friend.add_url(public_url)
                 if photo_url:
                     friend.set_photo_url(photo_url)
+                    
+                for number in extract_phone_numbers(p):
+                    friend.add_phone(number)
                 
                 friends.append(friend)
 
diff --git a/package/src/org/maemo/hermes/engine/phonenumber.py b/package/src/org/maemo/hermes/engine/phonenumber.py
new file mode 100644 (file)
index 0000000..da1b401
--- /dev/null
@@ -0,0 +1,488 @@
+import re
+
+class PhoneNumber():
+    """Encapsulate a phone number.
+    
+       Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
+       Released under the Artistic Licence."""
+
+    
+    def __init__(self, number, device=None, type=None, country=None):
+        """Store a phone number.
+        
+             'device' can be one of: None, 'landline', 'mobile'
+             'type' can be one of: None, 'home', 'work'
+             'country' can be a two-character ISO-3166 country code.
+        """
+        
+        self._country = country and country.lower() or None
+        self._number  = _build_number(number, self._country)
+        self._device  = device
+        self._type    = type
+    
+    # -----------------------------------------------------------------------
+    def __repr__(self):
+        device = self._device
+        type = self._type
+        if device and type:
+            return "Tel (%s %s): %s" % (type, device, self.get_number())
+        if device or type:
+            return "Tel (%s): %s" % (type or device, self.get_number())
+        return "Tel: %s" % self.get_number()
+
+    
+    # -----------------------------------------------------------------------
+    def get_device(self):
+        """Return the device type. Can be one of None, 'landline' or 'mobile'."""
+        
+        return self._device
+
+    
+    # -----------------------------------------------------------------------
+    def get_type(self):
+        """Return the number type. Can be one of None, 'home' or 'work'.""" 
+        
+        return self._type
+
+    
+    # -----------------------------------------------------------------------
+    def get_country(self):
+        """Return the country type.""" 
+        
+        return self._country
+
+    
+    # -----------------------------------------------------------------------
+    def get_number(self):
+        """Return the number, turning it into an international format
+           if possible.""" 
+        
+        return self._number
+
+
+# -----------------------------------------------------------------------
+_stripre = re.compile(r'[^\dp#\*]+')
+_dropre  = re.compile(r'^0')
+
+def _build_number(number, country):
+    """Return the number, turning it into an international format
+       if possible."""
+       
+    clean_number = _stripre.sub('', number)
+    dropped_zero = _dropre.sub('', clean_number)
+    if number.startswith('+'):
+        return '+%s' % (clean_number)
+    if country == 'af':
+        return '+93%s' % (clean_number)
+    if country == 'al':
+        return '+355%s' % (clean_number)
+    if country == 'dz':
+        return '+213%s' % (clean_number)
+    if country == 'ad':
+        return '+376%s' % (clean_number)
+    if country == 'ao':
+        return '+244%s' % (clean_number)
+    if country == 'aq':
+        return '+672%s' % (clean_number)
+    if country == 'ar':
+        return '+54%s' % (clean_number)
+    if country == 'am':
+        return '+374%s' % (clean_number)
+    if country == 'aw':
+        return '+297%s' % (clean_number)
+    if country == 'au':
+        return '+61%s' % (clean_number)
+    if country == 'at':
+        return '+43%s' % (clean_number)
+    if country == 'az':
+        return '+994%s' % (clean_number)
+    if country == 'bh':
+        return '+973%s' % (clean_number)
+    if country == 'bd':
+        return '+880%s' % (clean_number)
+    if country == 'by':
+        return '+375%s' % (clean_number)
+    if country == 'be':
+        return '+32%s' % (clean_number)
+    if country == 'bz':
+        return '+501%s' % (clean_number)
+    if country == 'bj':
+        return '+229%s' % (clean_number)
+    if country == 'bt':
+        return '+975%s' % (clean_number)
+    if country == 'bo':
+        return '+591%s' % (clean_number)
+    if country == 'ba':
+        return '+387%s' % (clean_number)
+    if country == 'bw':
+        return '+267%s' % (clean_number)
+    if country == 'br':
+        return '+55%s' % (clean_number)
+    if country == 'bn':
+        return '+673%s' % (clean_number)
+    if country == 'bg':
+        return '+359%s' % (clean_number)
+    if country == 'bf':
+        return '+226%s' % (clean_number)
+    if country == 'mm':
+        return '+95%s' % (clean_number)
+    if country == 'bi':
+        return '+257%s' % (clean_number)
+    if country == 'kh':
+        return '+855%s' % (clean_number)
+    if country == 'cm':
+        return '+237%s' % (clean_number)
+    if country == 'ca':
+        return '+1%s' % (clean_number)
+    if country == 'cv':
+        return '+238%s' % (clean_number)
+    if country == 'cf':
+        return '+236%s' % (clean_number)
+    if country == 'td':
+        return '+235%s' % (clean_number)
+    if country == 'cl':
+        return '+56%s' % (clean_number)
+    if country == 'cn':
+        return '+86%s' % (clean_number)
+    if country == 'cx':
+        return '+61%s' % (clean_number)
+    if country == 'cc':
+        return '+61%s' % (clean_number)
+    if country == 'co':
+        return '+57%s' % (clean_number)
+    if country == 'km':
+        return '+269%s' % (clean_number)
+    if country == 'cg':
+        return '+242%s' % (clean_number)
+    if country == 'cd':
+        return '+243%s' % (clean_number)
+    if country == 'ck':
+        return '+682%s' % (clean_number)
+    if country == 'cr':
+        return '+506%s' % (clean_number)
+    if country == 'hr':
+        return '+385%s' % (clean_number)
+    if country == 'cu':
+        return '+53%s' % (clean_number)
+    if country == 'cy':
+        return '+357%s' % (clean_number)
+    if country == 'cz':
+        return '+420%s' % (clean_number)
+    if country == 'dk':
+        return '+45%s' % (clean_number)
+    if country == 'dj':
+        return '+253%s' % (clean_number)
+    if country == 'tl':
+        return '+670%s' % (clean_number)
+    if country == 'ec':
+        return '+593%s' % (clean_number)
+    if country == 'eg':
+        return '+20%s' % (clean_number)
+    if country == 'sv':
+        return '+503%s' % (clean_number)
+    if country == 'gq':
+        return '+240%s' % (clean_number)
+    if country == 'er':
+        return '+291%s' % (clean_number)
+    if country == 'ee':
+        return '+372%s' % (clean_number)
+    if country == 'et':
+        return '+251%s' % (clean_number)
+    if country == 'fk':
+        return '+500%s' % (clean_number)
+    if country == 'fo':
+        return '+298%s' % (clean_number)
+    if country == 'fj':
+        return '+679%s' % (clean_number)
+    if country == 'fi':
+        return '+358%s' % (clean_number)
+    if country == 'fr':
+        return '+33%s' % (dropped_zero)
+    if country == 'pf':
+        return '+689%s' % (clean_number)
+    if country == 'ga':
+        return '+241%s' % (clean_number)
+    if country == 'gm':
+        return '+220%s' % (clean_number)
+    if country == 'ge':
+        return '+995%s' % (clean_number)
+    if country == 'de':
+        return '+49%s' % (clean_number)
+    if country == 'gh':
+        return '+233%s' % (clean_number)
+    if country == 'gi':
+        return '+350%s' % (clean_number)
+    if country == 'gr':
+        return '+30%s' % (clean_number)
+    if country == 'gl':
+        return '+299%s' % (clean_number)
+    if country == 'gt':
+        return '+502%s' % (clean_number)
+    if country == 'gn':
+        return '+224%s' % (clean_number)
+    if country == 'gw':
+        return '+245%s' % (clean_number)
+    if country == 'gy':
+        return '+592%s' % (clean_number)
+    if country == 'ht':
+        return '+509%s' % (clean_number)
+    if country == 'hn':
+        return '+504%s' % (clean_number)
+    if country == 'hk':
+        return '+852%s' % (clean_number)
+    if country == 'hu':
+        return '+36%s' % (clean_number)
+    if country == 'in':
+        return '+91%s' % (clean_number)
+    if country == 'id':
+        return '+62%s' % (clean_number)
+    if country == 'ir':
+        return '+98%s' % (clean_number)
+    if country == 'iq':
+        return '+964%s' % (clean_number)
+    if country == 'ie':
+        return '+353%s' % (clean_number)
+    if country == 'im':
+        return '+44%s' % (dropped_zero)
+    if country == 'il':
+        return '+972%s' % (clean_number)
+    if country == 'it':
+        return '+39%s' % (clean_number)
+    if country == 'ci':
+        return '+225%s' % (clean_number)
+    if country == 'jp':
+        return '+81%s' % (clean_number)
+    if country == 'jo':
+        return '+962%s' % (clean_number)
+    if country == 'kz':
+        return '+7%s' % (clean_number)
+    if country == 'ke':
+        return '+254%s' % (clean_number)
+    if country == 'ki':
+        return '+686%s' % (clean_number)
+    if country == 'kw':
+        return '+965%s' % (clean_number)
+    if country == 'kg':
+        return '+996%s' % (clean_number)
+    if country == 'la':
+        return '+856%s' % (clean_number)
+    if country == 'lv':
+        return '+371%s' % (clean_number)
+    if country == 'lb':
+        return '+961%s' % (clean_number)
+    if country == 'ls':
+        return '+266%s' % (clean_number)
+    if country == 'lr':
+        return '+231%s' % (clean_number)
+    if country == 'ly':
+        return '+218%s' % (clean_number)
+    if country == 'li':
+        return '+423%s' % (clean_number)
+    if country == 'lt':
+        return '+370%s' % (clean_number)
+    if country == 'lu':
+        return '+352%s' % (clean_number)
+    if country == 'mo':
+        return '+853%s' % (clean_number)
+    if country == 'mk':
+        return '+389%s' % (clean_number)
+    if country == 'mg':
+        return '+261%s' % (clean_number)
+    if country == 'mw':
+        return '+265%s' % (clean_number)
+    if country == 'my':
+        return '+60%s' % (clean_number)
+    if country == 'mv':
+        return '+960%s' % (clean_number)
+    if country == 'ml':
+        return '+223%s' % (clean_number)
+    if country == 'mt':
+        return '+356%s' % (clean_number)
+    if country == 'mh':
+        return '+692%s' % (clean_number)
+    if country == 'mr':
+        return '+222%s' % (clean_number)
+    if country == 'mu':
+        return '+230%s' % (clean_number)
+    if country == 'yt':
+        return '+262%s' % (clean_number)
+    if country == 'mx':
+        return '+52%s' % (clean_number)
+    if country == 'fm':
+        return '+691%s' % (clean_number)
+    if country == 'md':
+        return '+373%s' % (clean_number)
+    if country == 'mc':
+        return '+377%s' % (clean_number)
+    if country == 'mn':
+        return '+976%s' % (clean_number)
+    if country == 'me':
+        return '+382%s' % (clean_number)
+    if country == 'ma':
+        return '+212%s' % (clean_number)
+    if country == 'mz':
+        return '+258%s' % (clean_number)
+    if country == 'na':
+        return '+264%s' % (clean_number)
+    if country == 'nr':
+        return '+674%s' % (clean_number)
+    if country == 'np':
+        return '+977%s' % (clean_number)
+    if country == 'nl':
+        return '+31%s' % (clean_number)
+    if country == 'an':
+        return '+599%s' % (clean_number)
+    if country == 'nc':
+        return '+687%s' % (clean_number)
+    if country == 'nz':
+        return '+64%s' % (clean_number)
+    if country == 'ni':
+        return '+505%s' % (clean_number)
+    if country == 'ne':
+        return '+227%s' % (clean_number)
+    if country == 'ng':
+        return '+234%s' % (clean_number)
+    if country == 'nu':
+        return '+683%s' % (clean_number)
+    if country == 'kp':
+        return '+850%s' % (clean_number)
+    if country == 'no':
+        return '+47%s' % (clean_number)
+    if country == 'om':
+        return '+968%s' % (clean_number)
+    if country == 'pk':
+        return '+92%s' % (clean_number)
+    if country == 'pw':
+        return '+680%s' % (clean_number)
+    if country == 'pa':
+        return '+507%s' % (clean_number)
+    if country == 'pg':
+        return '+675%s' % (clean_number)
+    if country == 'py':
+        return '+595%s' % (clean_number)
+    if country == 'pe':
+        return '+51%s' % (clean_number)
+    if country == 'ph':
+        return '+63%s' % (clean_number)
+    if country == 'pn':
+        return '+870%s' % (clean_number)
+    if country == 'pl':
+        return '+48%s' % (clean_number)
+    if country == 'pt':
+        return '+351%s' % (clean_number)
+    if country == 'pr':
+        return '+1%s' % (clean_number)
+    if country == 'qa':
+        return '+974%s' % (clean_number)
+    if country == 'ro':
+        return '+40%s' % (clean_number)
+    if country == 'ru':
+        return '+7%s' % (clean_number)
+    if country == 'rw':
+        return '+250%s' % (clean_number)
+    if country == 'bl':
+        return '+590%s' % (clean_number)
+    if country == 'ws':
+        return '+685%s' % (clean_number)
+    if country == 'sm':
+        return '+378%s' % (clean_number)
+    if country == 'st':
+        return '+239%s' % (clean_number)
+    if country == 'sa':
+        return '+966%s' % (clean_number)
+    if country == 'sn':
+        return '+221%s' % (clean_number)
+    if country == 'rs':
+        return '+381%s' % (clean_number)
+    if country == 'sc':
+        return '+248%s' % (clean_number)
+    if country == 'sl':
+        return '+232%s' % (clean_number)
+    if country == 'sg':
+        return '+65%s' % (clean_number)
+    if country == 'sk':
+        return '+421%s' % (clean_number)
+    if country == 'si':
+        return '+386%s' % (clean_number)
+    if country == 'sb':
+        return '+677%s' % (clean_number)
+    if country == 'so':
+        return '+252%s' % (clean_number)
+    if country == 'za':
+        return '+27%s' % (clean_number)
+    if country == 'kr':
+        return '+82%s' % (clean_number)
+    if country == 'es':
+        return '+34%s' % (clean_number)
+    if country == 'lk':
+        return '+94%s' % (clean_number)
+    if country == 'sh':
+        return '+290%s' % (clean_number)
+    if country == 'pm':
+        return '+508%s' % (clean_number)
+    if country == 'sd':
+        return '+249%s' % (clean_number)
+    if country == 'sr':
+        return '+597%s' % (clean_number)
+    if country == 'sz':
+        return '+268%s' % (clean_number)
+    if country == 'se':
+        return '+46%s' % (clean_number)
+    if country == 'ch':
+        return '+41%s' % (clean_number)
+    if country == 'sy':
+        return '+963%s' % (clean_number)
+    if country == 'tw':
+        return '+886%s' % (clean_number)
+    if country == 'tj':
+        return '+992%s' % (clean_number)
+    if country == 'tz':
+        return '+255%s' % (clean_number)
+    if country == 'th':
+        return '+66%s' % (clean_number)
+    if country == 'tg':
+        return '+228%s' % (clean_number)
+    if country == 'tk':
+        return '+690%s' % (clean_number)
+    if country == 'to':
+        return '+676%s' % (clean_number)
+    if country == 'tn':
+        return '+216%s' % (clean_number)
+    if country == 'tr':
+        return '+90%s' % (clean_number)
+    if country == 'tm':
+        return '+993%s' % (clean_number)
+    if country == 'tv':
+        return '+688%s' % (clean_number)
+    if country == 'ae':
+        return '+971%s' % (clean_number)
+    if country == 'ug':
+        return '+256%s' % (clean_number)
+    if country == 'gb':
+        return '+44%s' % (dropped_zero)
+    if country == 'ua':
+        return '+380%s' % (clean_number)
+    if country == 'uy':
+        return '+598%s' % (clean_number)
+    if country == 'us':
+        return '+1%s' % (clean_number)
+    if country == 'uz':
+        return '+998%s' % (clean_number)
+    if country == 'vu':
+        return '+678%s' % (clean_number)
+    if country == 'va':
+        return '+39%s' % (clean_number)
+    if country == 've':
+        return '+58%s' % (clean_number)
+    if country == 'vn':
+        return '+84%s' % (clean_number)
+    if country == 'wf':
+        return '+681%s' % (clean_number)
+    if country == 'ye':
+        return '+967%s' % (clean_number)
+    if country == 'zm':
+        return '+260%s' % (clean_number)
+    if country == 'zw':
+        return '+263%s' % (clean_number)
+            
+    return clean_number
index 150deae..ccf2a6e 100644 (file)
@@ -153,4 +153,3 @@ class EVCardAttribute(Structure):
             return None
      
         return self.values.contents
-
index 675b316..0e26884 100644 (file)
@@ -7,6 +7,8 @@ from unit.test_gravatar import TestGravatarService
 from unit.test_linkedin import TestLinkedInService
 from unit.test_twitter import TestTwitterService
 from unit.test_hermes import TestHermes
+from unit.test_friend import TestFriend
+from unit.test_phonenumber import TestPhoneNumber
 
 from integration.test_gravatar import IntegrationTestGravatarService
 from integration.test_linkedinapi import IntegrationTestLinkedInApi
diff --git a/package/test/unit/test_friend.py b/package/test/unit/test_friend.py
new file mode 100644 (file)
index 0000000..5e950ab
--- /dev/null
@@ -0,0 +1,55 @@
+from org.maemo.hermes.engine.friend import Friend
+from org.maemo.hermes.engine.phonenumber import PhoneNumber
+import unittest
+
+class FakeContact():
+    def __init__(self, name):
+        self.name = name
+        self.urls = []
+        self.phones = []
+        self.photo = None
+        self.nickname = None
+        self.birthday = None
+    def add_url(self, url):
+        self.urls.append(url)
+        return 1
+    def get_urls(self):
+        return self.urls
+    def get_name(self):
+        return self.name
+    def get_identifiers(self):
+        return [canonical(self.name)]
+    def add_phone(self, phone):
+        self.phones.append(phone.get_number())
+        return 1
+    def get_phones(self):
+        return self.phones
+    def get_photo(self):
+        return self.photo
+    def set_photo(self, photo):
+        self.photo = photo
+    def get_nickname(self):
+        return self.nickname
+    def set_nickname(self, nickname):
+        self.nickname = nickname
+    def get_birthday(self):
+        return self.birthday
+    def set_birthday(self, d, m, y):
+        self.birthday = '%s-%s-%s' % (y, m, d)
+
+class TestFriend(unittest.TestCase):
+    
+    def test_that_contacts_are_updated(self):
+        friend = Friend(name = 'Bob Smith', source = 'test')
+        friend.add_url("http://chitter.com/bob.smith")
+        friend.add_url("http://www.friendface.com/bob.smith")
+        friend.add_phone(PhoneNumber("01234 567890"))
+        
+        contact = FakeContact('Person Person')
+        friend.update_contact(contact, False)
+        assert 'http://chitter.com/bob.smith' in contact.get_urls()
+        assert 'http://www.friendface.com/bob.smith' in contact.get_urls()
+
+    
+if __name__ == '__main__':
+    unittest.main()
diff --git a/package/test/unit/test_phonenumber.py b/package/test/unit/test_phonenumber.py
new file mode 100644 (file)
index 0000000..2f29fc4
--- /dev/null
@@ -0,0 +1,40 @@
+from org.maemo.hermes.engine.phonenumber import PhoneNumber
+import unittest
+
+class TestPhoneNumber(unittest.TestCase):
+    
+    def test_that_simple_number_is_returned(self):
+        num = PhoneNumber('01234 567890')
+        assert num.get_number() == '01234567890'
+        
+        
+    def test_other_properties(self):
+        num = PhoneNumber('01234 567890', type='home', device='landline', country = 'xx')
+        assert num.get_number() == '01234567890'
+        assert num.get_type() == 'home'
+        assert num.get_device() == 'landline'
+        assert num.get_country() == 'xx'
+        
+    def test_already_countrified(self):
+        num = PhoneNumber('+441234 567890')
+        assert num.get_number() == '+441234567890'
+
+        num = PhoneNumber('+441234 567890', country = 'fr')
+        assert num.get_number() == '+441234567890'
+        
+        
+    def test_country_code_formatting(self):
+        num = PhoneNumber('555-1234', country = 'us')
+        assert num.get_number() == '+15551234'
+        
+
+    def test_special_country_code_formatting(self):
+        num = PhoneNumber('(0)3 20 73 88 87', country = 'fr')
+        assert num.get_number() == '+33320738887'
+
+        num = PhoneNumber('01234 567890', country = 'gb')
+        assert num.get_number() == '+441234567890'
+
+    
+if __name__ == '__main__':
+    unittest.main()