Module vobjecttools
[hide private]
[frames] | no frames]

Source Code for Module vobjecttools

  1  """Additional layer to vobject site-package""" 
  2   
  3  from pisiconstants import * 
  4  import vobject 
  5  import datetime 
  6  from events import events 
  7   
  8  """Indentifies a phone entry as home phone""" 
  9  VCF_PHONETYPE_HOME = [['HOME', 'VOICE'], ['VOICE', 'HOME'], ['HOME']] 
 10  """Indentifies a phone entry as work phone""" 
 11  VCF_PHONETYPE_WORK = [['WORK', 'VOICE'], ['VOICE', 'WORK'], ['WORK']] 
 12  """Indentifies a phone entry as mobile phone""" 
 13  VCF_PHONETYPE_MOBILE = [['CELL', 'VOICE'], ['VOICE', 'CELL'], ['CELL'], ['VOICE']] 
 14  """Indentifies a phone entry as fax""" 
 15  VCF_PHONETYPE_FAX = [['FAX']] 
 16  """Entries to remove before comparing""" 
 17  VCF_PHONETYPE_IGNORELIST = ['OTHER'] 
 18   
 19  """Indentifies an address entry as home address""" 
 20  VCF_ADDRESSTYPE_HOME = [['HOME', 'POSTAL'], ['POSTAL', 'HOME'], ['HOME']] 
 21  """Indentifies an address entry as work address""" 
 22  VCF_ADDRESSTYPE_WORK = [['WORK', 'POSTAL'], ['POSTAL', 'WORK'], ['WORK']] 
 23   
 24   
25 -def _extractAtt(x, st):
26 """ 27 Supporting function for pulling information out of a attribute 28 29 Gets around problems with non available attributes without the need for checking this beforehand for each attribute. 30 """ 31 try: 32 ret = None 33 exec "ret = " + st 34 return ret 35 except AttributeError: 36 return ''
37 38 # 39 # PART 1: LOADING (Contacts) 40 # 41
42 -def extractVcfEntry(x, defaultPhonetype = None):
43 """ 44 Walks an entire vobject vcard entity and stores all information in a dictionary, which is returned in the end 45 46 For mapping the PISI rules are applied. 47 """ 48 atts = {} 49 atts['firstname'] = _extractAtt(x, 'x.n.value.given') 50 atts['middlename'] = _extractAtt(x, 'x.n.value.additional') 51 atts['lastname'] = _extractAtt(x, 'x.n.value.family') 52 53 atts['email'] = _extractAtt(x, 'x.email.value') 54 atts['title'] = _extractAtt(x, 'x.title.value') 55 56 # phone numbers 57 try: 58 for tel in x.contents['tel']: 59 if not tel.params.has_key('TYPE'): 60 if defaultPhonetype: 61 atts[defaultPhonetype] = tel.value 62 else: 63 for i in range(len(tel.params['TYPE'])): 64 tel.params['TYPE'][i] = tel.params['TYPE'][i].upper() 65 for ign in VCF_PHONETYPE_IGNORELIST: 66 try: 67 del tel.params['TYPE'][tel.params['TYPE'].index(ign)] 68 except ValueError: 69 pass # fine - this ignore value is not in the list 70 if tel.params['TYPE'] in VCF_PHONETYPE_HOME: 71 atts['phone'] = tel.value 72 elif tel.params['TYPE'] in VCF_PHONETYPE_MOBILE: 73 atts['mobile'] = tel.value 74 elif tel.params['TYPE'] in VCF_PHONETYPE_WORK: 75 atts['officePhone'] = tel.value 76 elif tel.params['TYPE'] in VCF_PHONETYPE_FAX: 77 atts['fax'] = tel.value 78 except KeyError: 79 pass # no phone number; that's alright 80 81 # addresses 82 try: 83 for addr in x.contents['adr']: 84 for i in range(len(addr.params['TYPE'])): 85 addr.params['TYPE'][i] = addr.params['TYPE'][i].upper() 86 if addr.params['TYPE'] in VCF_ADDRESSTYPE_HOME: 87 atts['homeStreet'] = addr.value.street 88 atts['homeCity'] = addr.value.city 89 atts['homeState'] = addr.value.region 90 atts['homePostalCode'] = addr.value.code 91 atts['homeCountry'] = addr.value.country 92 elif addr.params['TYPE'] in VCF_ADDRESSTYPE_WORK: 93 atts['businessStreet'] = addr.value.street 94 atts['businessCity'] = addr.value.city 95 atts['businessState'] = addr.value.region 96 atts['businessPostalCode'] = addr.value.code 97 atts['businessCountry'] = addr.value.country 98 except KeyError: 99 pass # no addresses here; that's fine 100 101 try: 102 atts['businessOrganisation'] = x.org.value[0] 103 atts['businessDepartment'] = x.org.value[1] 104 except: 105 pass 106 return atts
107 108 109 110 # 111 # PART 2: SAVING (Contacts) 112 # 113
114 -def _createRawAttribute(c, j, att, value, params = []):
115 """ 116 Supporting function for adding a single attribute to vcard raw object 117 """ 118 try: 119 v = None 120 if value != None: 121 exec ("v =" + value) 122 if v != None and v != '': 123 j.add(att) 124 exec ("j." + att + ".value = " + value) 125 for param in params: 126 exec("j." + att + "." + param[0] + "=" + param[1] ) 127 except KeyError: 128 pass # this attribute is not available; that's not a problem for us
129
130 -def _createNameAttribute(c, j):
131 family = c.attributes['lastname'] 132 if family == None: 133 family = '' 134 given = c.attributes['firstname'] 135 if given == None: 136 given = '' 137 additional = c.attributes['middlename'] 138 if additional == None: 139 additional = '' 140 if family == '' and given == '' and additional == '': 141 c.prettyPrint() 142 nameEntry = vobject.vcard.Name(family = family, given = given, additional = additional) 143 n = j.add('n') 144 n.value = nameEntry
145
146 -def _createPhoneAttribute(c, j, type):
147 """ 148 Create and append phone entry 149 150 Phone entries are a bit tricky - you cannot access each of them directly as they all have the same attribute key (tel). Consequently, we have to access the dictionary for phones directly. 151 """ 152 try: 153 if type in VCF_PHONETYPE_HOME: 154 value = c.attributes['phone'] 155 elif type in VCF_PHONETYPE_WORK: 156 value = c.attributes['officePhone'] 157 elif type in VCF_PHONETYPE_MOBILE: 158 value = c.attributes['mobile'] 159 elif type in VCF_PHONETYPE_FAX: 160 value = c.attributes['fax'] 161 if value == '' or value == None: 162 return 163 tel = j.add('tel') 164 tel.value = value 165 tel.type_param = type 166 except KeyError: 167 pass # that's fine - this phone type is not available
168
169 -def _attFromDict(dict, att):
170 try: 171 return dict[att] 172 except KeyError: 173 return ''
174
175 -def _createAddressAttribute(c, j, type):
176 """ 177 Create and append address entry 178 179 Entry is only created if city is set. 180 """ 181 if type in VCF_ADDRESSTYPE_HOME: 182 street = _attFromDict(c.attributes, 'homeStreet') 183 postalCode = _attFromDict(c.attributes, 'homePostalCode') 184 city = _attFromDict(c.attributes, 'homeCity') 185 country = _attFromDict(c.attributes, 'homeCountry') 186 state = _attFromDict(c.attributes, 'homeState') 187 elif type in VCF_ADDRESSTYPE_WORK: 188 street = _attFromDict(c.attributes, 'businessStreet') 189 postalCode = _attFromDict(c.attributes, 'businessPostalCode') 190 city = _attFromDict(c.attributes, 'businessCity') 191 country = _attFromDict(c.attributes, 'businessCountry') 192 state = _attFromDict(c.attributes, 'businessState') 193 if city == None or city == '': 194 return 195 addr = j.add('adr') 196 addr.value = vobject.vcard.Address(street = street, code = postalCode, city = city, country = country, region = state) 197 addr.type_param = type
198
199 -def _createBusinessDetails(c, j):
200 """ 201 Creates an entry for business organzation und unit. 202 """ 203 if c.attributes.has_key('businessOrganisation'): 204 o = c.attributes['businessOrganisation'] 205 if o == None or o == '': 206 return 207 list = [] 208 list.append(o) 209 if c.attributes.has_key('businessDepartment'): 210 ou = c.attributes['businessDepartment'] 211 if ou != None and ou != '': 212 list.append(ou) 213 j.add('org') 214 j.org.value = list
215
216 -def createRawVcard(c):
217 """ 218 Converts internal contact entry to VObject format 219 """ 220 j = vobject.vCard() 221 _createNameAttribute(c, j) 222 223 if c.attributes['firstname']: 224 if c.attributes['lastname']: 225 fn = c.attributes['firstname'] + ' ' + c.attributes['lastname'] 226 else: 227 fn = c.attributes['firstname'] 228 else: 229 fn = c.attributes['lastname'] 230 _createRawAttribute(c, j, 'fn', "'''" + fn + "'''") 231 _createRawAttribute(c, j, 'title', "c.attributes['title']") 232 233 _createRawAttribute(c, j, 'email', "c.attributes['email']", [['type_param', "'INTERNET'"]]) 234 _createPhoneAttribute(c, j, VCF_PHONETYPE_HOME[0]) 235 _createPhoneAttribute(c, j, VCF_PHONETYPE_WORK[0]) 236 _createPhoneAttribute(c, j, VCF_PHONETYPE_MOBILE[0]) 237 _createPhoneAttribute(c, j, VCF_PHONETYPE_FAX[0]) 238 239 _createAddressAttribute(c, j, VCF_ADDRESSTYPE_HOME[0]) 240 _createAddressAttribute(c, j, VCF_ADDRESSTYPE_WORK[0]) 241 _createBusinessDetails(c, j) 242 return j
243
244 -def checkForMandatoryFields(entries):
245 """ 246 Checks each entry, whether all mandatory fields are available. 247 248 Current implementation only chechs for Full Name - if not available it will try to assemble something from first and last name. 249 """ 250 for x in entries.keys(): 251 v = entries[x] 252 253 try: 254 v.fn 255 except AttributeError: 256 firstname = _extractAtt(v, 'x.n.value.given') 257 lastname = _extractAtt(v, 'x.n.value.family') 258 259 if firstname: 260 if lastname: 261 fn =firstname + ' ' + lastname 262 else: 263 fn = firstname 264 else: 265 fn = lastname 266 _createRawAttribute(None, v, 'fn', "'''" + fn + "'''")
267 268 269 # 270 # PART 3: LOADING (Calendar) 271 #
272 -def _extractRecurrence(x, allDay):
273 """ 274 Transforms VObject recurrence information into PISI internal object L{events.Recurrence} 275 """ 276 if _extractAtt(x, 'x.dtend.value'): 277 end = x.dtend 278 else: 279 end = None 280 rec = events.Recurrence() 281 rec.initFromAttributes(x.rrule, x.dtstart, end, allDay) 282 return rec
283
284 -def extractICSEntry(x):
285 atts = {} 286 atts['start'] = _extractAtt(x, 'x.dtstart.value') 287 atts['end'] = _extractAtt(x, 'x.dtend.value') 288 if type(x.dtstart.value) ==datetime.date: 289 atts['allday'] = True 290 else: 291 atts['allday'] = False 292 # For all stupid ICS files coming without timezone information 293 294 # start even more stupid 295 # some funny date entries within ics files cannot be parsed by vobject 296 # I realized that with Mobical Syncml for instance 297 if type (atts['start']) == unicode: 298 if atts['start'].endswith('Z'): 299 atts['start'] = datetime.datetime.strptime(atts['start'], "%Y%m%dT%H%M%SZ") 300 else: 301 atts['start'] = datetime.datetime.strptime(atts['start'], "%Y%m%dT%H%M%S") 302 if type (atts['end']) == unicode: 303 if atts['end'].endswith('Z'): 304 atts['end'] = datetime.datetime.strptime(atts['end'], "%Y%m%dT%H%M%SZ") 305 else: 306 atts['end'] = datetime.datetime.strptime(atts['end'], "%Y%m%dT%H%M%S") 307 # end even more stupid 308 309 if atts['start'].tzinfo == None: 310 atts['start'] = atts['start'].replace(tzinfo = events.UTC()) 311 if atts['end'].tzinfo == None: 312 atts['end'] = atts['end'].replace(tzinfo = events.UTC()) 313 atts['title'] = _extractAtt(x, 'x.summary.value') 314 atts['description'] = _extractAtt(x, 'x.description.value') 315 atts['location'] = _extractAtt(x, 'x.location.value') 316 try: 317 atts['alarmmin'] = x.valarm.trigger.value.days * 24 * 60 + x.valarm.trigger.value.seconds / 60 318 atts['alarm'] = True 319 except AttributeError: 320 atts['alarm'] = False 321 atts['alarmmin'] = 0 322 323 try: 324 atts['recurrence'] = _extractRecurrence(x, atts['allday'] ) 325 except AttributeError: 326 atts['recurrence'] = None 327 updated = _extractAtt(x, 'x.last_modified.value') 328 329 if x.contents.has_key('x-pisi-id'): 330 globalId = x.contents['x-pisi-id'][0].value 331 else: 332 globalId = None 333 334 return atts, globalId, updated
335 336 337 # 338 # PART 4: SAVING (Calendar) 339 # 340
341 -def _createRecurrencePart(c, cal):
342 """ 343 Transforms PISI internal recurrence information (L{events.Recurrence}) into vobject representation 344 """ 345 if c.attributes['recurrence']: 346 rec = c.attributes['recurrence'] 347 cal.add('rrule') 348 cal.rrule = rec.getRRule()
349
350 -def _createAlarmPart(c, cal):
351 """ 352 Transforms PISI internal alarm information (1 single integer for minutes) into vobject representation 353 """ 354 if c.attributes['alarm']: 355 mins = c.attributes['alarmmin'] 356 days = mins / (24 * 60) 357 seconds = (mins - days*(24*60)) * 60 358 cal.add("valarm") 359 cal.valarm.add("trigger") 360 cal.valarm.trigger.value=datetime.timedelta(days,seconds)
361
362 -def createRawEventEntry(c, stupidMode = False):
363 """ 364 Transforms PISI internal Calendar event information (L{events.Event}) into vobject representation 365 """ 366 frame = vobject.iCalendar() 367 frame.add('vevent') 368 cal = frame.vevent 369 cal.add('dtstart') 370 if c.attributes['allday']: 371 if type(c.attributes['start']) == datetime.datetime: 372 c.attributes['start'] = c.attributes['start'].date() 373 if type(c.attributes['end']) == datetime.datetime: 374 c.attributes['end'] = c.attributes['end'].date() 375 cal.dtstart.value = c.attributes['start'] # all day is applied automatically due to datetime.datetime or datetime.date class 376 cal.add('dtend') 377 cal.dtend.value = c.attributes['end'] 378 if c.attributes['title']: 379 cal.add('summary') 380 cal.summary.value = c.attributes['title'] 381 if c.attributes['description']: 382 cal.add('description') 383 cal.description.value = c.attributes['description'] 384 if c.attributes['location']: 385 cal.add('location') 386 cal.location.value = c.attributes['location'] 387 cal.add('x-pisi-id') 388 cal.contents['x-pisi-id'][0].value = c.attributes['globalid'] 389 _createRecurrencePart(c, cal) 390 _createAlarmPart(c, cal) 391 cal.add('last-modified') 392 cal.last_modified.value = datetime.datetime.now(events.UTC()) 393 return cal
394