User:Jaffa/Hermes plugins

Contents

Background

Hermes currently supports two services: Facebook and Twitter. However, there are patches for both Hyves[1] and Gravatar[2], as well as requests for LinkedIn[3], MySpace[4], Plaxo[5] and LiveJournal[6]. Further services will almost certainly be required.

The existing Hermes source is fairly geared towards Facebook and Twitter, and so there are both architectural and UI challenges in supporting many more services.

Requirements

  1. Ability for the user to select which services to use and the priority of them[7]
  2. Optional configuration screen for each service, e.g. authentication information
  3. UI shouldn't be overly generic to avoid same mistakes of Microfeed.

UI design

  • Change the "Accounts" dialogue to be like the sharing plugins: a list of icon-containing services which are enabled, with an "Add" button, a "Remove" button, "Move Up/Down" buttons and an "Edit" button which is enabled when an item is selected and further preferences are available.
  • Each service needs to provide an interface for opening the preferences for it, if any. For Facebook this'd be "Create birthday-only contacts", an "Authorise"/"Deauthorise" button.
  • The existing "Fetching friends' info" would be changed to a progress-bar: "Initialising services" with information on each service being initialised.

Code design

See early discussion on #6990.

The existing code in contacts.py would be migrated into a Contact class. This would, in addition to providing access to the various fields of concern, cache the name variants and provide a method for checking matches:

<syntaxhighlight lang=python>

 def matches_identifier(id):
   """Returns true if the Unicode-normalised, lower-case, non-alphabetic
      stripped characters of 'id' matches a name variant of this contact.
      Fields included in the name variants are:
        * First name
        * Last name
        * Nickname
        * Email address (with post-@ dot-suffices removed."""

</syntaxhighlight>

A Friend class would be introduced to give better defined access to name, service and picture. This is used in manual mapping, and for communication between the controller and the service. It would also store the contacts to which it was mapped, if any.

A root class provides meta-data about the service (such as name and icon) and access to the further UI configuration dialogue (if any) and the backend of the service.

The service backend will implement an interface based on a visitor pattern:

<syntaxhighlight lang=python>

 def get_friends(self):
   """Return a list of friends from this service, or 'None' if manual mapping
      is not supported."""
 def process_contact(self, contact, overwrite = false, friend = None):
   """Called for each contact in the address book. Any friends linked to
      from the contact should have their matching updated. The backend should 
      enrich the contact with any meta-data it can; and return 'True' if any
      information was updated. If 'friend' is provided, the information should
      be retrieved from friend. This will be one of the entries from 
      'get_friends' when manual mapping is being done."""
 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."""

</syntaxhighlight>

The main engine, Hermes, would then do something like the following pseudo-code:

<syntaxhighlight lang=python>

 friends = ()
 for service in services:
   for friend in service.get_friends():
     friends.add(friend)
 all_contacts = get_contacts_as_set()
 contacts = set()
 updated_contacts = set()
 for econtact in addressbook.get_all_contacts():
   contact = Contact(addressbook, econtact)
   contacts.add(contact)
   for service in something.get_services_by_prioritisation():
     if service.process_contact(contact):
       updated_contacts.add(contact)
 for service in something.get_services_by_prioritisation():
   service.finalise(updated_contacts)
 for contact in updated_contacts:
   contact.save()

</syntaxhighlight>

Some kind of mapping to a dict would then be made of friends when showing the GUI.

Unanswered questions

  • Should some X-HERMES-... fields be used to indicate what Hermes can overwrite[8]; if so, what if the user does want to overwrite some info? For matching, URLs should be sufficient for most services.
  • Should process_contact return a dict of updated fields? Then the engine can decide what to update based on prioritisation etc.

Notional implementations

Services such as Facebook, Twitter, Hyves and LinkedIn would contact upstream during construction (similar to the existing model).

Services such as Gravatar would contact upstream during finalisation, once the set of contacts with email addresses (and without photos if overwrite is false) has been identified.

References

  1. #6995 - Request for Hyves as a data source
  2. #5983 - Pull down images from gravatar.com...
  3. #6990 - Request for LinkedIn as a data source
  4. #6994 - Request for MySpace as a data source
  5. #6991 - Request for Plaxo as a data source
  6. #6992 - Request for LiveJournal as a data source
  7. #6990.c4, Fredrik Wendt
  8. #5452 - Don't overwrite pre-existing contact info