Dependency inject service ID, so that it can be stamped on friends and
[hermes] / package / src / org / maemo / hermes / engine / facebook / service.py
index 6f754e9..6fa6c68 100644 (file)
@@ -1,49 +1,32 @@
-import gnome.gconf
 import org.maemo.hermes.engine.service
 
-from facebook import Facebook,FacebookError
 from org.maemo.hermes.engine.names import canonical
 from org.maemo.hermes.engine.friend import Friend
 
 class Service(org.maemo.hermes.engine.service.Service):
     """Facebook backend for Hermes.
                 
-       This requires two gconf paths to contain Facebook application keys:
-           /apps/maemo/hermes/key_app
-           /apps/maemo/hermes/key_secret
-       
        Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
+       Copyright (c) Fredrik Wendt <fredrik@wendt.se> 2010.
        Released under the Artistic Licence."""
        
-       
+    attrs = ['uid', 'name', 'pic_big', 'birthday_date', 'profile_url', 'first_name', 'last_name', 'website']
+
+
     # -----------------------------------------------------------------------
-    def __init__(self, autocreate = False, gui_callback = None):
+    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._gc  = gnome.gconf.client_get_default()
-        self._gui = gui_callback
-        self._autocreate = autocreate
+        self.fb = facebook
+        self._service_id = service_id
         
-        # -- Check the environment is going to work...
-        #
-        if (self._gc.get_string('/desktop/gnome/url-handlers/http/command') == 'epiphany %s'):
-            raise Exception('Browser in gconf invalid (see NB#136012). Installation error.')
-
-        key_app    = self._gc.get_string('/apps/maemo/hermes/key_app')
-        key_secret = self._gc.get_string('/apps/maemo/hermes/key_secret')
-        if key_app is None or key_secret is None:
-            raise Exception('No Facebook application keys found. Installation error.')
-
-        self.fb = Facebook(key_app, key_secret)
-        self.fb.desktop = True
-        
-        self._friends = None
+        self._friends_by_name = {}
         self._friends_by_url = {}
         self._friends_by_contact = {}
-        self._should_create = set()
-        
-        self.get_friends()
+        self._contacts_by_friend = {}
+        self._friends_without_contact = set()
+        self._known_urls = set()
 
 
     # -----------------------------------------------------------------------
@@ -52,136 +35,90 @@ class Service(org.maemo.hermes.engine.service.Service):
     
 
     # -----------------------------------------------------------------------
-    def _do_fb_login(self):
-        """Perform authentication against Facebook and store the result in gconf
-             for later use. Uses the 'need_auth' and 'block_for_auth' methods on
-             the callback class. The former allows a message to warn the user
-             about what is about to happen to be shown; the second is to wait
-             for the user to confirm they have logged in."""
-        self.fb.session_key = None
-        self.fb.secret = None
-        self.fb.uid = None
-        
-        if self._gui:
-            self._gui.need_auth()
-            
-        self.fb.auth.createToken()
-        self.fb.login()
-        
-        if self._gui:
-            self._gui.block_for_auth()
-          
-        session = self.fb.auth.getSession()
-        self._gc.set_string('/apps/maemo/hermes/session_key', session['session_key'])
-        self._gc.set_string('/apps/maemo/hermes/secret_key', session['secret'])
-        self._gc.set_string('/apps/maemo/hermes/uid', str(session['uid']))
-
-   
-    # -----------------------------------------------------------------------
     def get_friends(self):
-        """Return a list of friends from this service, or 'None' if manual mapping
-           is not supported."""
-           
-        if self._friends:
-            return self._friends.values()
-         
-        if self.fb.session_key is None:
-            self.fb.session_key = self._gc.get_string('/apps/maemo/hermes/session_key')
-            self.fb.secret = self._gc.get_string('/apps/maemo/hermes/secret_key')
-            self.fb.uid = self._gc.get_string('/apps/maemo/hermes/uid')
+        return self._contacts_by_friend.keys()
+    
+    
+    def get_contacts_with_match(self):
+        return self._friends_by_contact
+    
+    def get_unmatched_friends(self):
+        return self._friends_by_name.values()
+
+
+    # -----------------------------------------------------------------------
+    def pre_process_contact(self, contact):
+        """Registers URLs of all previous mappings, and makes sure that any Friends with those
+           URLs don't get match by name."""
+        for url in contact.get_urls():
+            self._known_urls.add(url)
+    
+    
+    # -----------------------------------------------------------------------
+    def process_friends(self):
         
-        # Check the available session is still valid...
-        while True:
-            try:
-                if self.fb.users.getLoggedInUser() and self.fb.session_key:
-                    break
-            except FacebookError:
-                pass
-            self._do_fb_login()
-            
-            
         def if_defined(data, key, callback):
             if key in data and data[key]:
                 callback(data[key])
         
-        self._friends = {}
-        attrs = ['uid', 'name', 'pic_big', 'birthday_date', 'profile_url', 'first_name', 'last_name', 'website']
-        for data in self.fb.users.getInfo(self.fb.friends.get(), attrs):
-            key = canonical(data['name'])
-            friend = Friend(data['name'])
-            self._friends[key] = friend
+        friends_data = self._get_friends_data()
+        for data in friends_data:
+            key = canonical(data['name']) # FIXME: deal with name collision
+            friend = self._create_friend(data['name'])
         
             if 'profile_url' not in data:
                 data['profile_url'] = "http://www.facebook.com/profile.php?id=" + str(data['uid'])
         
-            self._friends_by_url[data['profile_url']] = friend
-            
-
             if_defined(data, 'website', friend.add_url)
             if_defined(data, 'profile_url', friend.add_url)
             if_defined(data, 'birthday_date', friend.set_birthday_date)
 
-            # exception from the rule:
-#            if_defined(data, 'pic_big', friend.set_photo_url)
-            friend.set_photo_url(data[attrs[2]])
+            if_defined(data, 'pic_big', friend.set_photo_url)
             
-            if friend.has_birthday_date():
-                self._should_create.add(friend)
-                
-        return self._friends.values()
+            if friend.has_birthday_date(): # FIXME: remove this, either you want to add your contacts or not? 
+                self._friends_without_contact.add(friend)
+            url = data['profile_url']
+            self._friends_by_url[url] = friend
+            
+            if url not in self._known_urls:
+                self._friends_by_name[key] = friend
 
 
     # -----------------------------------------------------------------------
-    def pre_process_contact(self, contact):
-        
-        if not self._friends:
-            self.get_friends()
-        
-        for url in contact.get_urls():
-            if url in self._friends_by_url:
-                matched_friend = self._friends_by_url[url]
-                self._friends_by_contact[contact] = matched_friend
-                self._should_create.discard(matched_friend)
-        
-    
-    # -----------------------------------------------------------------------
-    def process_contact(self, contact, friend):
-           
-        if not self._friends:
-            self.get_friends()
-
+    def process_contact(self, contact):
+        matched_friend = None
         if self._friends_by_contact.has_key(contact):
-            friend.update(self._friends_by_contact[contact])
-            return
+            matched_friend = self._friends_by_contact[contact]
         
-        matched_friend = None
-        # we might get a hit if the friend has setup a URL with another service 
-        for url in contact.get_urls():
-            if url in self._friends_by_url:
-                matched_friend = self._friends_by_url[url]
-                self._friends_by_contact[contact] = matched_friend
-                break
+        # we might get a hit if the friend has setup a URL with another service,
+        # such as putting the id link to Facebook on the Twitter account's profile
+        if not matched_friend:
+            for url in contact.get_urls():
+                if url in self._friends_by_url:
+                    matched_friend = self._friends_by_url[url]
+                    self._register_match(contact, matched_friend)
+                    break
 
         if not matched_friend:
             for id in contact.get_identifiers():
-                if id in self._friends:
-                    matched_friend = self._friends[id]
-                    self._friends_by_contact[contact] = matched_friend
+                if id in self._friends_by_name:
+                    matched_friend = self._friends_by_name.pop(id)
+                    self._register_match(contact, matched_friend)
                     break
                 
-        if matched_friend:
-            print contact.get_name(), " -> ", matched_friend
-            self._should_create.discard(matched_friend)
-        
-            
-    
+        return matched_friend
     
+
+    # -----------------------------------------------------------------------
+    def _register_match(self, contact, friend):
+        friend.set_contact(contact)
+        self._friends_without_contact.discard(friend)
+        self._friends_by_contact[contact] = friend
+        self._contacts_by_friend[friend] = contact
+
+
     # -----------------------------------------------------------------------
-    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
-           be added to."""
-
-        if True or self._autocreate: # FIXME - remove True or
-            for friend in self._should_create:
-                print "Need to autocreate: %s (%s)" % (friend._attributes, friend._multi_attributes)
+    def _get_friends_data(self):
+        """Returns a list of dicts, where each dict represents a friend/contact"""
+        
+        return self.fb.users.getInfo(self.fb.friends.get(), Service.attrs)