Changeset 2059
- Timestamp:
- 05/27/07 14:20:27 (19 months ago)
- Files:
-
- 1 modified
-
plugins/moto-sync/motosync.py (modified) (21 diffs)
Legend:
- Unmodified
- Added
- Removed
-
plugins/moto-sync/motosync.py
r2056 r2059 15 15 import xml.dom.minidom 16 16 from datetime import date, datetime, timedelta, time as datetime_time 17 import dateutil.parser, dateutil.rrule , dateutil.tz17 import dateutil.parser, dateutil.rrule as rrule, dateutil.tz 18 18 import opensync 19 19 … … 80 80 VCAL_DAYS = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'] 81 81 82 # days of week as constants in the dateutil.rrule module 83 RRULE_DAYS = [rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR, rrule.SA, rrule.SU] 84 82 85 # repeat types in the calendar 83 86 MOTO_REPEAT_NONE = 0 … … 180 183 181 184 def getXMLField(doc, tagname, subtag=None): 182 """Returns text in a given XML tag, or ''if not set.185 """Returns text in a given XML tag, or None if not set. 183 186 184 187 The XML structure that it looks for is: … … 189 192 elts = doc.getElementsByTagName(tagname) 190 193 if elts == []: 191 return ''194 return None 192 195 elt = elts[0] 193 196 if subtag: 194 197 children = elt.getElementsByTagName(subtag) 195 198 if children == []: 196 return ''199 return None 197 200 elt = children[0] 198 201 return getXMLText(elt) … … 275 278 """ 276 279 277 # XXX FIXME: whole function needs reworkign for new XML format278 return (MOTO_REPEAT_NONE, [])279 280 280 # can't support multiple rules 281 281 if len(rulenodes) != 1: … … 283 283 rulenode = rulenodes[0] 284 284 285 # build hash of rule parts 286 rules = {} 287 for node in rulenode.childNodes: 288 key = node.nodeName.lower() 289 val = getXMLText(node).lower() 290 if key[:1] == 'by': 291 val = set(val.split(',')) 292 rules[key] = val 293 294 # extract the parts 295 assert(rules.has_key('content')) # required 296 freq = rules['content'].lower() 297 bymonth = rules.get('bymonth') 298 byweekno = rules.get('byweekno') 299 byyearday = rules.get('byyearday') 300 bymonthday = rules.get('bymonthday') 301 byday = rules.get('byday') 285 # extract rule parts 286 freqstr = getXMLField(rulenode, 'Frequency').lower() 287 if freqstr == 'daily': 288 freq = rrule.DAILY 289 elif freqstr == 'weekly': 290 freq = rrule.WEEKLY 291 elif freqstr == 'monthly': 292 freq = rrule.MONTHLY 293 elif freqstr == 'yearly': 294 freq = rrule.YEARLY 295 else: 296 assert(False, "invalid frequency %s" % freqstr) 297 298 until = getXMLField(rulenode, 'Until') 299 if until: 300 until = parse_ical_time(until) 301 count = getXMLField(rulenode, 'Count') 302 if count: 303 count = int(count) 304 interval = getXMLField(rulenode, 'Interval') 305 if interval: 306 interval = int(interval) 307 308 def getSet(name, convfunc=int): 309 """Internal convenience function, get a set of values from XML.""" 310 val = getXMLField(rulenode, name) 311 if not val: 312 return None 313 return set(map(convfunc, val.split(','))) 314 315 bymonth = getSet('ByMonth') 316 byweekno = getSet('ByWeekNo') 317 byyearday = getSet('ByYearDay') 318 bymonthday = getSet('ByMonthDay') 319 byday = getSet('ByDay', lambda s: s.upper()) 302 320 303 321 # fail the conversion if any of these are set 304 if ( rules.has_key('byhour') or rules.has_key('byminute')305 or rules.has_key('bysecond') or rules.has_key('bysetpos')306 or rules.get('interval', '1') != '1'):322 if (getXMLField(rulenode, 'ByHour') or getXMLField(rulenode, 'ByMinute') 323 or getXMLField(rulenode, 'BySecond') or getXMLField(rulenode, 'BySetPos') 324 or (interval is not None and interval != 1)): 307 325 return (MOTO_REPEAT_NONE, []) 308 326 … … 320 338 321 339 # now test if the rule matches what we can represent 322 if (freq == 'daily'and (not byday or byday == byalldays)340 if (freq == rrule.DAILY and (not byday or byday == byalldays) 323 341 and not bymonthday and not byyearday and not byweekno 324 342 and not bymonth): 325 343 moto_type = MOTO_REPEAT_DAILY 326 elif (freq == 'weekly'and (not byday or byday == set([eventday]))344 elif (freq == rrule.WEEKLY and (not byday or byday == set([eventday])) 327 345 and not bymonthday and not byyearday and not byweekno and 328 346 not bymonth): 329 347 moto_type = MOTO_REPEAT_WEEKLY 330 elif (freq == 'monthly'and not byday348 elif (freq == rrule.MONTHLY and not byday 331 349 and (not bymonthday or bymonthday == set([eventdt.day])) 332 350 and not byyearday and not byweekno 333 351 and (not bymonth or bymonth == byallmonths)): 334 352 moto_type = MOTO_REPEAT_MONTHLY_DATE 335 elif (freq == 'monthly'and byday == byeventweekday and not bymonthday353 elif (freq == rrule.MONTHLY and byday == byeventweekday and not bymonthday 336 354 and not byyearday and not byweekno 337 355 and (not bymonth or bymonth == byallmonths)): 338 356 moto_type = MOTO_REPEAT_MONTHLY_DAY 339 elif (freq == 'yearly'and not byday357 elif (freq == rrule.YEARLY and not byday 340 358 and (not bymonthday or bymonthday == set([eventdt.day])) 341 359 and not byyearday and not byweekno … … 348 366 # now we need to work out the exceptions and see if this event still occurs 349 367 350 # recombine the rule parts into a single string, and parse into an rruleset 351 # XXX FIXME: totally different rrule format 352 # rulestr = ';'.join(map(getXMLText, rulenode.getElementsByTagName('Rule'))) 353 # ruleset = dateutil.rrule.rrulestr(rulestr, dtstart=eventdt, forceset=True) 368 # convert byday strings to constants needed by rrule 369 byweekday = [] 370 for bydaystr in byday: 371 day = RRULE_DAYS[VCAL_DAYS.index(bydaystr[-2:])] 372 nth = bydaystr[:-2] 373 if nth: 374 byweekday.append(day(int(nth))) 375 else: 376 byweekday.append(day) 377 378 # create an rrule object for this rule, and an rrule set 379 ruleobj = rrule.rrule(freq, dtstart=eventdt, count=count, until=until, 380 bymonth=list(bymonth), bymonthday=list(bymonthday), 381 byweekday=byweekday, byyearday=list(byyearday), 382 byweekno=list(byweekno)) 383 ruleset = rrule.rruleset() 384 ruleset.rrule(ruleobj) 354 385 355 386 # are there any future occurrences of this event? … … 368 399 # XXX FIXME: totally different rrule format 369 400 #for node in exrules: 370 # ruleset.exrule( dateutil.rrule.rrulestr(getXMLField(node, 'Content')))401 # ruleset.exrule(rrule.rrulestr(getXMLField(node, 'Content'))) 371 402 372 403 # are there any future occurrences of this event if we consider exceptions? … … 919 950 top = doc.documentElement 920 951 952 # compute the week number that the event falls in 953 if self.eventdt.day % 7 == 0: 954 weeknum = self.eventdt.day / 7 955 else: 956 weeknum = self.eventdt.day / 7 + 1 957 921 958 if self.alarmdt: 922 959 alarm = doc.createElement('Alarm') … … 953 990 # create an rrule object for this recurrence 954 991 if self.repeat_type == MOTO_REPEAT_MONTHLY_DATE: 955 rrule = dateutil.rrule.rrule(dateutil.rrule.MONTHLY, 956 bymonthday=self.eventdt.day, 957 dtstart=self.eventdt) 992 rule = rrule.rrule(rrule.MONTHLY, bymonthday=self.eventdt.day, 993 dtstart=self.eventdt) 958 994 elif self.repeat_type == MOTO_REPEAT_MONTHLY_DAY: 959 weekday = dateutil.rrule.weekdays[self.eventdt.weekday()] 960 # weeknum is calculated above in the XML generation 961 rrule = dateutil.rrule.rrule(dateutil.rrule.MONTHLY, 962 byweekday=weekday(+weeknum), 963 dtstart=self.eventdt) 995 weekday = rrule.weekdays[self.eventdt.weekday()] 996 rule = rrule.rrule(rrule.MONTHLY, byweekday=weekday(+weeknum), 997 dtstart=self.eventdt) 964 998 else: 965 999 if self.repeat_type == MOTO_REPEAT_DAILY: 966 freq = dateutil.rrule.DAILY1000 freq = rrule.DAILY 967 1001 elif self.repeat_type == MOTO_REPEAT_WEEKLY: 968 freq = dateutil.rrule.WEEKLY1002 freq = rrule.WEEKLY 969 1003 elif self.repeat_type == MOTO_REPEAT_YEARLY: 970 freq = dateutil.rrule.YEARLY971 r rule = dateutil.rrule.rrule(freq, dtstart=self.eventdt)1004 freq = rrule.YEARLY 1005 rule = rrule.rrule(freq, dtstart=self.eventdt) 972 1006 973 1007 # work out which dates the exceptions correspond to and … … 976 1010 e.setAttribute('Value', 'DATE') 977 1011 for exnum in self.exceptions: 978 appendXMLChild(doc, e, 'Content', format_time(r rule[exnum], VCAL_DATE))1012 appendXMLChild(doc, e, 'Content', format_time(rule[exnum], VCAL_DATE)) 979 1013 if e.hasChildNodes(): 980 1014 top.appendChild(e) … … 991 1025 appendXMLChild(doc, e, 'Frequency', 'MONTHLY') 992 1026 day = VCAL_DAYS[self.eventdt.weekday()] 993 # compute the week number that the event falls in994 if self.eventdt.day % 7 == 0:995 weeknum = self.eventdt.day / 7996 else:997 weeknum = self.eventdt.day / 7 + 1998 1027 appendXMLChild(doc, e, 'ByDay', '%d%s' % (weeknum, day)) 999 1028 elif self.repeat_type == MOTO_REPEAT_YEARLY: … … 1069 1098 duration = event.getElementsByTagName('Duration')[0] 1070 1099 def toint(numstr): 1071 """Convert a string to an integer, unless it's empty, in which case return 0."""1072 if numstr == '':1100 """Convert a string to an integer, unless it's None, in which case return 0.""" 1101 if numstr is None: 1073 1102 return 0 1074 1103 else: … … 1082 1111 else: 1083 1112 endstr = getField('DateEnd') 1084 if endstr != '':1113 if endstr is not None: 1085 1114 self.duration = parse_ical_time(endstr) - self.eventdt 1086 1115 else: … … 1099 1128 1100 1129 triggerstr = getField('Alarm', 'AlarmTrigger') 1101 if triggerstr == '':1130 if triggerstr is None: 1102 1131 self.alarmdt = None 1103 1132 else: … … 1320 1349 # handle the name and formatted name fields 1321 1350 self.name = getField('FormattedName') 1322 if self.name != '':1351 if self.name: 1323 1352 # FIXME: cheesy attempt at taking apart the FormattedName 1324 1353 last = getField('Name', 'LastName') 1325 if last != '':1354 if last: 1326 1355 lastidx = self.name.find(last) 1327 1356 if lastidx == 0: 1328 1357 first = getField('Name', 'FirstName') 1329 if first != '':1358 if first: 1330 1359 firstidx = self.name.find(first) 1331 1360 if firstidx != -1: … … 1338 1367 # compute FormattedName from Name 1339 1368 nameparts = [getField('Name', p) for p in XML_NAME_PARTS] 1340 nameparts = [p != '' for p in nameparts]1369 nameparts = [p for p in nameparts if p] 1341 1370 self.name = ' '.join(nameparts) 1342 1371 self.firstlast_enabled = 0 1343 1372 self.firstlast_index = self.name.index(getField('Name', 'LastName')) 1344 1373 1345 catname = getField('Categories', 'Category').lower() 1374 catname = getField('Categories', 'Category') 1375 if catname: 1376 catname = catname.lower() 1346 1377 self.categorynum = revcategories.get(catname, MOTO_CATEGORY_DEFAULT) 1347 1378 self.nickname = getField('Nickname') 1348 1379 bdaystr = getField('Birthday') 1349 if bdaystr != '':1380 if bdaystr: 1350 1381 self.birthday = parse_ical_time(bdaystr) 1351 1382 else: … … 1463 1494 else: 1464 1495 birthdaystr = '' 1496 if self.parent.nickname: 1497 nickname = self.parent.nickname 1498 else: 1499 nickname = '' 1465 1500 if self.address: 1466 (street1, street2, city, state, postcode, country) = self.address 1501 # remove None parts 1502 address = [] 1503 for part in self.address: 1504 if part: 1505 address.append(part) 1506 else: 1507 address.append('') 1508 (street1, street2, city, state, postcode, country) = address 1467 1509 else: 1468 1510 street1 = street2 = city = state = postcode = country = '' … … 1473 1515 self.parent.firstlast_index, self.picture_path, 1474 1516 0, 0, street2, street1, city, state, postcode, country, 1475 self.parent.nickname, birthdaystr)1517 nickname, birthdaystr) 1476 1518 1477 1519 def child_xml(self, doc): … … 1823 1865 1824 1866 ret = getXMLField(doc, 'device').strip() 1825 if ret == '':1867 if not ret: 1826 1868 raise opensync.Error('device not specified in config file', opensync.ERROR_MISCONFIGURATION) 1827 1869 return ret
