7b5093565d7aa151b62a039955642c576dcd7583
[hermes] / package / src / org / maemo / hermes / engine / hermes.py
1 from org.maemo.hermes.engine.friend import Friend
2 from org.maemo.hermes.engine.contact import Contact
3 import evolution
4
5 class Hermes:
6     """Encapsulate the process of syncing online friends' information with the
7        Evolution contacts' database. This should be used as follows:
8        
9          * Initialise, passing in a GUI callback.
10          * Call Syncjob.
11          * Retrieve information on changes effected.
12          * Call update_contact to enact manual mapping.
13          
14        Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
15        Released under the Artistic Licence."""
16
17     
18     # -----------------------------------------------------------------------
19     def __init__(self, services, gui_progress=None):
20         """Constructor. Passed a list of services, and a            
21            method which will be invoked with three arguments:
22                 str    Name of current step
23                 int    Current position
24                 int    Maximum value of position."""
25
26         
27         # -- These fields are currently part of the API...
28         #
29         self.updated = []
30         self.matched = []
31         self.unmatched = []
32         self.friends = {}
33         self.address_book = None
34         
35         # -- Other initialisation...
36         #
37         self._services = services
38         self._progress = gui_progress or (lambda msg, i, j: None)
39     
40     
41     # -----------------------------------------------------------------------
42     def run(self, overwrite_existing_fields=False):
43         self._progress("Reading contacts...", 1, 10000)
44         
45         contacts = []
46         self.address_book = self._get_address_book()
47         for econtact in self.address_book.get_all_contacts():
48             contacts.append(self._create_contact_wrapper(econtact))
49
50         # work out progress bar info
51         total_contacts = len(contacts) * len(self._services)
52         total_ticks    = 6 * total_contacts # Number of distinct loops below
53
54         # warm up
55         current_tick = 1
56         for service in self._services:
57             print "pre-process:", service.get_id()
58             for contact in contacts:
59                 self._progress("Pre-processing contacts...", current_tick, total_ticks)
60                 current_tick += 1
61                 service.pre_process_contact(contact)
62                 
63         # fetch data
64         for service in self._services:
65             print "process_friends:", service.get_id()
66             self._progress("Reading friends...", current_tick, total_ticks)
67             current_tick += len(contacts)
68             service.process_friends()
69         
70         # combine results into one friend
71         for contact in contacts:
72             result = Friend()
73             for service in self._services:
74                 print "process_contact:", service.get_id()
75                 self._progress("Processing contacts...", current_tick, total_ticks)
76                 current_tick += 1
77                 friend = service.process_contact(contact)
78                 if friend:
79                     contact.add_mapping(service.get_id())
80                     result.update_from_friend(friend)
81             
82             if result.get_name() is not None:
83                 self.update_contact(contact, result, overwrite_existing_fields)
84             else:
85                 self.unmatched.append(contact)
86             
87         # give services a chance to create new contacts
88         for service in self._services:
89             print "create_contacts:", service.get_id()
90             to_create = service.get_friends_to_create_contacts_for()
91             tick_increment = len(contacts) / (len(to_create) or 1)
92             print tick_increment, to_create
93             for friend in to_create:
94                 friend.set_source(service.get_id())
95                 self._progress("Creating contacts...", current_tick, total_ticks)
96                 current_tick += tick_increment
97                 self.create_contact_from_friend(friend)
98                 
99         # finalisation
100         for service in self._services:
101             print "finalize:", service.get_id()
102             self._progress("Finalising...", current_tick, total_ticks)
103             current_tick += len(contacts)
104             service.finalise(self.updated, overwrite_existing_fields)
105             for friend in service.get_unmatched_friends():
106                 friend.set_source(service.get_id())
107                 key = unicode(friend.get_name()).encode('trans') + "_" + service.get_id()
108                 self.friends[key] = friend
109             
110         # commit changes
111         tick_increment = total_contacts / (len(self.updated) or 1)
112         print tick_increment, self.updated
113         for contact in self.updated:
114             print "committing changes to:", contact.get_name(), contact
115             self._progress("Saving changes...", current_tick, total_ticks)
116             current_tick += tick_increment
117             self.address_book.commit_contact(contact.get_econtact())
118             
119         self._progress('Finished', 1, -1)
120         
121
122     # -----------------------------------------------------------------------
123     def update_contact(self, contact, friend, resync=False, commit=False):
124         """Update the given contact with information from the given friend."""
125         
126         print "updating contact ", contact, " with friend ", friend
127         if friend.update_contact(contact, resync):
128             self.updated.append(contact)
129             if commit:
130                 self.address_book.commit_contact(contact.get_econtact())
131
132         self.matched.append(contact)
133         if friend.get_source() is not None:
134             contact.add_mapping(friend.get_source())
135
136
137     # -----------------------------------------------------------------------
138     def create_contact_from_friend(self, friend):
139         econtact = self._create_empty_contact(friend)
140         contact = self._create_contact_wrapper(econtact)
141                 
142         self.address_book.add_contact(contact.get_econtact())
143         self.update_contact(contact, friend)
144         
145         print "Created [%s]" % (contact.get_name())
146         
147         
148     # -----------------------------------------------------------------------
149     def _get_address_book(self):
150         return evolution.ebook.open_addressbook('default')
151
152     # -----------------------------------------------------------------------
153     def _create_empty_contact(self, friend):
154         econtact = evolution.ebook.EContact()
155         econtact.props.given_name = friend['first_name']
156         econtact.props.family_name = friend['last_name']
157         econtact.props.full_name = friend.get_name()
158         return econtact
159     
160     # -----------------------------------------------------------------------
161     def _create_contact_wrapper(self, econtact):
162         return Contact(self.address_book, econtact)