added Friend (a data container)
[hermes] / package / src / org / maemo / hermes / engine / contact.py
1 import urllib
2 import Image
3 import ImageOps
4 import StringIO
5 import datetime
6 import re
7 from org.maemo.hermes.engine.names import canonical, variants
8 from pygobject import *
9 from ctypes import *
10
11 # Constants from http://library.gnome.org/devel/libebook/stable/EContact.html#EContactField
12 ebook = CDLL('libebook-1.2.so.5')
13 E_CONTACT_HOMEPAGE_URL = 42
14 E_CONTACT_PHOTO = 94
15 E_CONTACT_EMAIL = 97
16 E_CONTACT_BIRTHDAY_DATE = 107
17
18
19 class Contact:
20     """Provide an abstraction of contact, working around limitations
21        in the evolution-python bindings. Properties are defined at:
22        
23           http://library.gnome.org/devel/libebook/stable/EContact.html#id3066469
24
25        Copyright (c) Andrew Flegg <andrew@bleb.org> 2009.
26        Released under the Artistic Licence."""
27
28        
29     # -----------------------------------------------------------------------
30     def __init__(self, book, contact):
31         """Create a new contact store for modifying contacts in the given
32            EBook."""
33         
34         self._book = book
35         self._contact = contact
36         self._identifiers = self._find_ids()
37         
38         
39     # -----------------------------------------------------------------------
40     def _find_ids(self):
41         """Return a set of the identifiers which should be used to match this
42            contact. Includes variants of first name, last name, nickname and
43            email address. These are all Unicode-normalised into the traditional
44            US-ASCII namespace"""
45            
46         result = set()
47         for name in variants(self._contact.get_name()):
48             result.add(canonical(name))
49             
50         for name in variants(self._contact.get_property('nickname')):
51             result.add(canonical(name))
52             
53         for email in self.get_emails():
54             user = canonical(email.split('@')[0])
55             if len(user) > 4:
56                 result.add(user)
57
58         return result
59     
60     
61     # -----------------------------------------------------------------------
62     def get_name(self):
63         """Return this contact's name."""
64         
65         return self._contact.get_name() or self._contact.get_property('nickname')
66     
67     
68     # -----------------------------------------------------------------------
69     def get_identifiers(self):
70         """Return the lowercase, Unicode-normalised, all-alphabetic
71            versions of identifiers for this contact."""
72            
73         return self._identifiers
74     
75     
76     # -----------------------------------------------------------------------
77     def set_photo(self, url):
78         """Set the given contact's photo to the picture found at the URL. If the
79            photo is wider than it is tall, it will be cropped with a bias towards
80            the top of the photo."""
81         
82         try:
83             f = urllib.urlopen(url)
84             data = ''
85             while True:
86                 read_data = f.read()
87                 data += read_data
88                 if not read_data:
89                     break
90             
91             im = Image.open(StringIO.StringIO(data))
92             (w, h) = im.size
93             if (h > w):
94                 ##print u"Shrinking photo for %s as it's %d x %d" % (self._contact.get_name(), w, h)
95                 im = ImageOps.fit(im, (w, w), Image.NEAREST, 0, (0, 0.1))
96               
97             ## print u"Updating photo for %s" % (self._contact.get_name())
98             f = StringIO.StringIO()
99             im.save(f, "JPEG")
100             image_data = f.getvalue()
101             photo = EContactPhoto()
102             photo.type = 0
103             photo.data = EContactPhoto_data()
104             photo.data.inlined = EContactPhoto_inlined()
105             photo.data.inlined.mime_type = cast(create_string_buffer("image/jpeg"), c_char_p)
106             photo.data.inlined.length = len(image_data)
107             photo.data.inlined.data = cast(create_string_buffer(image_data), c_void_p)
108             ebook.e_contact_set(hash(self._contact), E_CONTACT_PHOTO, addressof(photo))
109             return True
110         except:
111             print "FAILED to get photo from URL %s" % url
112             return False
113     
114     
115     # -----------------------------------------------------------------------
116     def has_photo(self):
117         # FIXME
118         return False
119       
120       
121     # -----------------------------------------------------------------------
122     def set_birthday(self, day, month, year = 0):
123         """Set the birthday for this contact to the given day, month and year."""
124         
125         if year == 0:
126             year = datetime.date.today().year
127           
128         birthday = EContactDate()
129         birthday.year = year
130         birthday.month = month
131         birthday.day = day
132         print u"Setting birthday for [%s] to %d-%d-%d" % (self._contact.get_name(), year, month, day)
133         ebook.e_contact_set(hash(self._contact), E_CONTACT_BIRTHDAY_DATE, addressof(birthday))
134         return True
135     
136     
137     # -----------------------------------------------------------------------
138     def set_nickname(self, nickname):
139         """Set the nickname for this contact to the given nickname."""
140         
141         # FIXME does this work?
142         self._contact.set_property('nickname', nickname)
143         #ebook.e_contact_set(hash(self._contact), E_NICKNAME, addressof(nickname))
144     
145     
146     # -----------------------------------------------------------------------
147     def get_emails(self):
148         """Return the email addresses associated with this contact."""
149         
150         emails = []
151         ai = GList.new(ebook.e_contact_get_attributes(hash(self._contact), E_CONTACT_EMAIL))
152         while ai.has_next():
153             attr = ai.next(as_a = EVCardAttribute)
154             if not attr:
155                 raise Exception(u"Unexpected null attribute for [" + self._contact.get_name() + "] with emails " + emails)
156             emails.append(string_at(attr.value().next()))
157           
158         return emails
159         
160       
161       
162     # -----------------------------------------------------------------------
163     def get_urls(self):
164         """Return a list of URLs which are associated with this contact."""
165         
166         urls = []
167         ai = GList.new(ebook.e_contact_get_attributes(hash(self._contact), E_CONTACT_HOMEPAGE_URL))
168         while ai.has_next():
169             attr = ai.next(as_a = EVCardAttribute)
170             if not attr:
171                 raise Exception(u"Unexpected null attribute for [" + self._contact.get_name() + "] with URLs " + urls)
172             urls.append(string_at(attr.value().next()))
173           
174         return urls
175     
176       
177     # -----------------------------------------------------------------------
178     def add_url(self, str, unique = ''):
179         """Add a new URL to the set of URLs for the given contact."""
180         
181         urls = re.findall('(?:(?:ftp|https?):\/\/|\\bwww\.|\\bftp\.)[,\w\.\-\/@:%?&=%+#~_$\*]+[\w=\/&=+#]', str, re.I | re.S)
182         updated = False
183         for url in urls:
184             updated = self._add_url(url, unique or re.sub('(?:.*://)?(\w+(?:[\w\.])*).*', '\\1', url)) or updated
185         
186         return updated
187     
188     
189     # -----------------------------------------------------------------------
190     def _add_url(self, url, unique):
191         """Do the work of adding a unique URL to a contact."""
192         
193         url_attr = None
194         ai = GList.new(ebook.e_contact_get_attributes(hash(self._contact), E_CONTACT_HOMEPAGE_URL))
195         while ai.has_next():
196             attr = ai.next(as_a = EVCardAttribute)
197             existing = string_at(attr.value().next())
198             #print "Existing URL [%s] when adding [%s] to [%s] with constraint [%s]" % (existing, url, contact.get_name(), unique)
199             if existing == unique or existing == url:
200                 return False
201             elif existing.find(unique) > -1:
202                 url_attr = attr
203           
204         if not url_attr:
205             ai.add()
206             url_attr = EVCardAttribute()
207             url_attr.group = ''
208             url_attr.name = 'URL'
209         
210         val = GList()
211         ##print u"Setting URL for [%s] to [%s]" % (self._contact.get_name(), url)
212         val.set(create_string_buffer(url))
213         ai.set(addressof(url_attr))
214         url_attr.values = cast(addressof(val), POINTER(GList))
215         ebook.e_contact_set_attributes(hash(self._contact), E_CONTACT_HOMEPAGE_URL, addressof(ai))
216         return True
217