Dependency inject service ID, so that it can be stamped on friends and
authorAndrew Flegg <andrew@bleb.org>
Wed, 9 Jun 2010 07:07:42 +0000 (08:07 +0100)
committerAndrew Flegg <andrew@bleb.org>
Wed, 9 Jun 2010 08:10:14 +0000 (09:10 +0100)
queried in hermes.
Add additional steps to Hermes.run_alt() so that it should now be
complete from an API point of view.
Pass API through to LinkedIn service and fix Twitter service creation.

18 files changed:
package/src/org/maemo/hermes/engine/facebook/provider.py
package/src/org/maemo/hermes/engine/facebook/service.py
package/src/org/maemo/hermes/engine/friend.py
package/src/org/maemo/hermes/engine/gravatar/provider.py
package/src/org/maemo/hermes/engine/gravatar/service.py
package/src/org/maemo/hermes/engine/hermes.py
package/src/org/maemo/hermes/engine/linkedin/provider.py
package/src/org/maemo/hermes/engine/linkedin/service.py
package/src/org/maemo/hermes/engine/service.py
package/src/org/maemo/hermes/engine/twitter/provider.py
package/src/org/maemo/hermes/engine/twitter/service.py
package/src/org/maemo/hermes/gui/gtkui.py
package/test/integration/test_gravatar.py
package/test/integration/test_twitter.py
package/test/unit/test_facebook.py
package/test/unit/test_gravatar.py
package/test/unit/test_linkedin.py
package/test/unit/test_twitter.py

index 820ef26..21d24f6 100644 (file)
@@ -114,7 +114,7 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
                 pass
             self._do_fb_login()
 
-        return org.maemo.hermes.engine.facebook.service.Service(self.fb, self._gc.get_bool('/apps/maemo/hermes/facebook_birthday_only'))
+        return org.maemo.hermes.engine.facebook.service.Service(self.get_id(), self.fb, self._gc.get_bool('/apps/maemo/hermes/facebook_birthday_only'))
 
 
     # -----------------------------------------------------------------------
index fa12cc2..6fa6c68 100644 (file)
@@ -14,11 +14,12 @@ class Service(org.maemo.hermes.engine.service.Service):
 
 
     # -----------------------------------------------------------------------
-    def __init__(self, facebook, create_birthday_only = False):
+    def __init__(self, service_id, facebook, create_birthday_only = False):
         """Initialise the Facebook service, finding Facebook API keys in gconf and
            having a gui_callback available."""
         
         self.fb = facebook
+        self._service_id = service_id
         
         self._friends_by_name = {}
         self._friends_by_url = {}
index 957819f..32ee3c2 100644 (file)
@@ -26,6 +26,9 @@ class Friend():
     def get_name(self):
         return self._attributes['fn']
     
+    def get_source(self):
+        return self._source
+    
     def get_nickname(self):
         return self._attributes["nickname"]
     
index 4627c28..96e81e9 100644 (file)
@@ -37,4 +37,4 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
         api_email = self._gconf.get_string('/apps/maemo/hermes/gravatar_email')
         api_key = self._gconf.get_string('/apps/maemo/hermes/gravatar_key')
 
-        return org.maemo.hermes.engine.gravatar.service.Service(api_email, api_key)
+        return org.maemo.hermes.engine.gravatar.service.Service(self.get_id(), api_email, api_key)
index 24132cb..f8f336c 100644 (file)
@@ -10,14 +10,17 @@ class Service(org.maemo.hermes.engine.service.Service):
     _image_url_base = "http://www.gravatar.com/avatar.php?"
        
     # -----------------------------------------------------------------------
-    def __init__(self, api_email, api_key):
+    def __init__(self, service_id, api_email, api_key):
         """Initialise the Gravatar service.
         
         api_email is the email address to use when talking to the backend.
         api_key is the "secret" key used when talking to the backend
         """
-        self._api_email = api_email
-        self._api_key = api_key
+        
+        org.maemo.hermes.engine.service.Service.__init__(self, service_id)
+
+        self._api_email  = api_email
+        self._api_key    = api_key
         if self._api_key is None or self._api_email is None:
             raise Exception('No Gravatar application keys found. Installation error.')
          
@@ -30,11 +33,6 @@ class Service(org.maemo.hermes.engine.service.Service):
         self._contacts_by_friend = {}
         
         self._api_url = 'https://secure.gravatar.com/xmlrpc?user=' + hashlib.md5(self._api_email).hexdigest()
-   
-
-    # -----------------------------------------------------------------------
-    def get_name(self):
-        return "Gravatar"
     
     
     # -----------------------------------------------------------------------
index 732b04d..0d9034b 100644 (file)
@@ -66,28 +66,51 @@ class Hermes:
     # -----------------------------------------------------------------------
     def run_alt(self, overwrite_existing_fields=False):
         contacts = []
-        book = evolution.ebook.open_addressbook('default')
-        for contact in book.get_all_contacts():
-            contacts.append(Contact(book, contact))
+        self.addresses = evolution.ebook.open_addressbook('default')
+        for contact in self.addresses.get_all_contacts():
+            contacts.append(Contact(self.addresses, contact))
 
         # warm up
         for service in self._services:
+            print "pre-process:", service.get_id()
             for contact in contacts:
                 service.pre_process_contact(contact)
                 
         # fetch data
         for service in self._services:
+            print "process_friends:", service.get_id()
             service.process_friends()
         
         # combine results into one friend
         for contact in contacts:
             result = Friend()
             for service in self._services:
+                print "process_contact:", service.get_id()
                 friend = service.process_contact(contact)
-                if (friend):
+                if friend:
+                    contact.add_mapping(service.get_id())
                     friend.decorate(result)
             
-            self.update_contact(friend, overwrite_existing_fields)
+            if result.get_name() is not None:
+                self.update_contact(result, overwrite_existing_fields)
+            else:
+                self.unmatched.add(contact)
+            
+        # give services a chance to create new contacts
+        for service in self._services:
+            print "create_contacts:", service.get_id()
+            for friend in service.create_contacts():
+                self.create_contact_from_friend(friend)
+                
+        # finalisation
+        for service in self._services:
+            print "finalize:", service.get_id()
+            service.finalise(self.updated, overwrite_existing_fields)
+            
+        # commit changes
+        for contact in self.updated:
+            print "committing changes to:", contact.get_name(), contact
+            self.addresses.commit_contact(contact.get_econtact())
         
 
     # -----------------------------------------------------------------------
@@ -95,22 +118,21 @@ class Hermes:
         """Update the given contact with information from the given friend."""
         
         print "updating contact ", contact, " with friend ", friend
+        self.updated.append(contact)
+        self.matched.append(contact)
+        if friend.get_source() is not None:
+            contact.add_mapping(friend.get_source())
 
+
+    # -----------------------------------------------------------------------
     def create_contact_from_friend(self, friend):
-        contact = evolution.ebook.EContact()
-        contact.props.full_name = friend['name']
-        contact.props.given_name = friend['first_name']
-        contact.props.family_name = friend['last_name']
-        
-        self.update_contact(contact, friend)
-        
+        econtact = evolution.ebook.EContact()
+        econtact.props.full_name = friend['name']
+        econtact.props.given_name = friend['first_name']
+        econtact.props.family_name = friend['last_name']
+        contact = Contact(self.addresses, econtact)
+                
         self.addresses.add_contact(contact)
-        self.updated.append(contact)
-        self.addresses.commit_contact(contact)
+        self.update_contact(contact, friend)
         
         print "Created [%s]" % (contact.get_name())
-        self.matched.append(contact)
-
-
-    def commit(self):
-        self.store.close()
index 382a04e..d10a946 100644 (file)
@@ -17,6 +17,7 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
         """Initialise the provider, and ensure the environment is going to work."""
 
         self._gc = gnome.gconf.client_get_default()
+        self._api = LinkedInApi(gconf = self._gc) 
 
 
     # -----------------------------------------------------------------------
@@ -47,15 +48,14 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
         """Open the preferences for this provider as a child of the 'parent' widget."""
 
         self.main_window = parent
-        api = LinkedInApi(gconf = self._gc) 
         dialog = gtk.Dialog(self.get_name(), parent)
         dialog.add_button(_('Disable'), gtk.RESPONSE_NO)
         enable = dialog.add_button(_('Enable'), gtk.RESPONSE_YES)
     
         button = hildon.Button(gtk.HILDON_SIZE_FINGER_HEIGHT,
                                hildon.BUTTON_ARRANGEMENT_VERTICAL)
-        self._handle_button(None, api, button, enable)
-        button.connect('clicked', self._handle_button, api, button, enable)
+        self._handle_button(None, button, enable)
+        button.connect('clicked', self._handle_button, button, enable)
             
         dialog.vbox.add(gtk.Label(""))
         dialog.vbox.add(button)
@@ -71,17 +71,17 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
 
 
     # -----------------------------------------------------------------------
-    def _handle_button(self, e, api, button, enable):
+    def _handle_button(self, e, button, enable):
         """Ensure the button state is correct."""
         
-        authenticated = api.get_access_token_from_gconf() is not None
+        authenticated = self._api.get_access_token_from_gconf() is not None
         if e is not None:
             if authenticated:
-                api.remove_access_token_from_gconf()
+                self._api.remove_access_token_from_gconf()
             else:
-                api.authenticate(lambda: None, self.block_for_auth)
+                self._api.authenticate(lambda: None, self.block_for_auth)
         
-            authenticated = api.get_access_token_from_gconf() is not None
+            authenticated = self._api.get_access_token_from_gconf() is not None
         
         button.set_title(authenticated and _("Clear authorisation") or _("Authorise"))
         enable.set_sensitive(authenticated)
@@ -92,7 +92,7 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
         """Part of the GUI callback API."""
 
         webbrowser.open(url)
-        time.sleep(2)
+        time.sleep(3)
         note = gtk.Dialog(_('Service authorisation'), self.main_window)
         note.add_button(_("Validate"), gtk.RESPONSE_OK)
         input = hildon.Entry(gtk.HILDON_SIZE_FINGER_HEIGHT)
@@ -113,4 +113,4 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
     def service(self, gui_callback):
         """Return the service backend."""
            
-        return org.maemo.hermes.engine.linkedin.service.Service()
+        return org.maemo.hermes.engine.linkedin.service.Service(self.get_id(), self._api)
index ff91285..d3d9666 100644 (file)
@@ -11,11 +11,11 @@ class Service(org.maemo.hermes.engine.facebook.service.Service):
        
        
     # -----------------------------------------------------------------------
-    def __init__(self, linkedInApi):
+    def __init__(self, service_id, linkedInApi):
         """Initialize the LinkedIn service, finding LinkedIn API keys in gconf and
            having a gui_callback available."""
         
-        org.maemo.hermes.engine.facebook.service.Service.__init__(self, None)
+        org.maemo.hermes.engine.facebook.service.Service.__init__(self, service_id, None)
         self.linkedInApi = linkedInApi
 
     
index 9075920..85469b1 100644 (file)
@@ -7,15 +7,17 @@ class Service:
        Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
        Released under the Artistic Licence."""
        
-    def __init__(self):
+    def __init__(self, service_id):
         """Should make initial calls to the service and retrieve a list of friends."""
+        
+        self._service_id = service_id
 
 
     # -----------------------------------------------------------------------
-    def get_name(self):
-        """Should return the same name as the provider class - debugging only - REMOVE/FIXME"""
+    def get_id(self):
+        """Return the service ID, as given to the service at creation."""
         
-        return None
+        return self._service_id
 
     
     # -----------------------------------------------------------------------
@@ -51,6 +53,14 @@ class Service:
 
 
     # -----------------------------------------------------------------------
+    def create_contacts(self):
+        """Return a list of friends which should have contacts created. This
+           should expected by the user, and optional."""
+           
+        return []
+
+
+    # -----------------------------------------------------------------------
     def finalise(self, updated, overwrite = False):
         """Once all contacts have been processed, allows for any tidy-up/additional
            enrichment. If any contacts are updated at this stage, 'updated' should
@@ -64,4 +74,4 @@ class Service:
     def _create_friend(self, name):
         """Used to create a Friend object for a contact"""
         
-        return Friend(name)
+        return Friend(name, self._service_id)
index 58386e0..87508ed 100644 (file)
@@ -107,4 +107,4 @@ class Provider(org.maemo.hermes.engine.provider.Provider):
         
         api = twitter.Api(username=username, password=password)
 
-        return org.maemo.hermes.engine.twitter.service.Service(username, password, gui_callback)
+        return org.maemo.hermes.engine.twitter.service.Service(self.get_id(), api)
\ No newline at end of file
index f5d16a5..9042076 100644 (file)
@@ -10,7 +10,9 @@ class Service(org.maemo.hermes.engine.service.Service):
        
        
     # -----------------------------------------------------------------------
-    def __init__(self, twitterApi):
+    def __init__(self, service_id, twitterApi):
+        org.maemo.hermes.engine.service.Service.__init__(self, service_id)
+
         self._twitter = twitterApi
         
         self._friends_by_name = {}
@@ -18,11 +20,6 @@ class Service(org.maemo.hermes.engine.service.Service):
         self._friends_by_contact = {}
         self._friends = []
         self._known_urls = set()
-
-   
-    # -----------------------------------------------------------------------
-    def get_name(self):
-        return "Twitter"
     
     
     # -----------------------------------------------------------------------
index 90bdd30..6804d45 100644 (file)
@@ -75,8 +75,6 @@ class HermesGUI(WimpWorks):
                 for provider in enabled:
                     services.append(provider.service(self))
                     
-                print services
-                
                 hermes = Hermes(services, self.progress)
                 hermes.run_alt(force)
                 gobject.idle_add(self.open_summary, hermes)
index 7a774d3..5f828e6 100644 (file)
@@ -13,7 +13,7 @@ class FakeContact():
 class IntegrationTestGravatarService(unittest.TestCase):
     
     def setUp(self):
-        self.testee = Service('maemohermes@wendt.se', 'b14ec179822b')
+        self.testee = Service('gravatar', 'maemohermes@wendt.se', 'b14ec179822b')
         
         self.existing_address = 'fredrik@wendt.se'
         self.existing_contact = FakeContact([self.existing_address])
index cb6f3e0..0872abb 100644 (file)
@@ -8,7 +8,7 @@ class IntegrationTestTwitterService(unittest.TestCase):
     
     def setUp(self):
         self.api = twitter.Api(username="maemohermes", password="Eha8ohr7Cu")
-        self.testee = Service(self.api)
+        self.testee = Service('twitter', self.api)
     
     
     def test_main_flow(self):
index 93255bb..b0e5f95 100644 (file)
@@ -22,7 +22,7 @@ class FakeContact():
 class TestFacebookService(unittest.TestCase):
     
     def setUp(self):
-        self.testee = Service(None)
+        self.testee = Service("facebook", None)
         
         
     def test_that_process_known_contact_returns_friend_object(self):
index 3de6b32..ffc70bd 100644 (file)
@@ -71,7 +71,7 @@ class TestGravatarService(unittest.TestCase):
         return self._server_response
 
     def _setUp(self, email, key):
-        self.testee = Service(email, key)
+        self.testee = Service('gravatar', email, key)
         
         self.existing_address = 'fredrik@wendt.se'
         self.existing_contact = FakeContact([self.existing_address])
index 63b57f7..5f6f4c0 100644 (file)
@@ -33,7 +33,7 @@ class TestLinkedInService(unittest.TestCase):
     
     def setUp(self):
         self.linkedInApi = FakeLinkedInApi()
-        self.testee = Service(self.linkedInApi)
+        self.testee = Service('linkedin', self.linkedInApi)
 
 
     def test_that_process_contact_returns_None_for_unknown_contact(self):
index 5a3be33..bd38307 100644 (file)
@@ -34,7 +34,7 @@ class FakeTweeter():
 class TestTwitterService(unittest.TestCase):
     
     def setUp(self):
-        self.testee = Service(None)
+        self.testee = Service('twitter', None)
     
     
     def test_that_process_contact_returns_None_for_unknown_contact(self):