Changeset 1796
- Timestamp:
- 02/22/07 01:42:57 (2 years ago)
- Files:
-
- plugins/moto-sync/motosync.py (modified) (27 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
plugins/moto-sync/motosync.py
r1793 r1796 47 47 # object types supported by this plugin 48 48 SUPPORTED_OBJTYPES = ['event', 'contact'] 49 50 CAPABILITIES = """<?xml version="1.0"?> 51 <capabilities> 52 <contact> 53 <Address /> 54 <Birthday /> 55 <Categories /> 56 <EMail /> 57 <FormattedName /> 58 <Name /> 59 <Nickname /> 60 <Telephone /> 61 </contact> 62 <event> 63 <Summary /> 64 <DateStarted /> 65 <DateEnd /> 66 <Duration /> 67 <Alarm /> 68 <RecurrenceRule /> 69 <ExceptionDateTime /> 70 </event> 71 </capabilities> 72 """ 49 73 50 74 # my phone doesn't like it if you read more than this many entries at a time … … 79 103 ] 80 104 81 # mapping from phone's contact type to vcard number types 82 MOTO_CONTACT_TYPES = { 83 0: 'work', 84 1: 'home', 85 2: 'voice', 86 3: 'cell', 87 4: 'fax', 88 5: 'pager', 89 11: 'voice' # shows up as "other", seen on a RAZR V3x 105 # mapping from phone's contact type to XML number types 106 MOTO_PHONE_CONTACT_TYPES = { 107 2: 'Voice', 108 3: 'Cellular', 109 4: 'Fax', 110 5: 'Pager', 111 11: 'Voice' # shows up as "other", seen on a RAZR V3x 112 } 113 114 # as above, but to TelephoneLocation 115 MOTO_PHONE_CONTACT_LOCATIONS = { 116 0: 'Work', 117 1: 'Home' 90 118 } 91 119 92 120 # reverse of the above (almost): mapping from vcard to phone's contact type 93 121 VCARD_CONTACT_TYPES = { 94 ' work':0,95 ' home':1,96 ' voice':2,97 ' cell':3,98 ' car':3,99 ' pcs': 3,100 ' fax':4,101 ' pager':5,122 'Work': 0, 123 'Home': 1, 124 'Voice': 2, 125 'Cellular': 3, 126 'Car': 3, 127 'Message': 5, 128 'Fax': 4, 129 'Pager': 5, 102 130 } 103 131 … … 105 133 # can also have dom/intl/postal/parcel... these don't really make sense here 106 134 VCARD_ADDRESS_TYPES = { 107 ' work': 0,108 ' home': 1,135 'Work': 0, 136 'Home': 1, 109 137 } 110 138 111 139 # reverse of the above 112 140 MOTO_ADDRESS_TYPES = { 113 0: ' work',114 1: ' home',141 0: 'Work', 142 1: 'Home', 115 143 } 116 144 … … 139 167 XML_NAME_PARTS = 'Prefix FirstName Additional LastName Suffix'.split() 140 168 XML_ADDRESS_PARTS = ('Street ExtendedAddress' 141 + ' City Region PostalCode Country').split()169 + ' Locality Region PostalCode Country').split() 142 170 143 171 # legal characters in telephone numbers … … 264 292 """ 265 293 294 # XXX FIXME: whole function needs reworkign for new XML format 295 return (MOTO_REPEAT_NONE, []) 296 266 297 # can't support multiple rules 267 298 if len(rulenodes) != 1: … … 271 302 # build hash of rule parts 272 303 rules = {} 273 for rule in map(getXMLText, rulenode.getElementsByTagName('Rule')):274 key , val = rule.split('=', 1)275 key = key.lower()304 for node in rulenode.childNodes: 305 key = node.nodeName.lower() 306 val = getXMLText(node).lower() 276 307 if key[:1] == 'by': 277 308 val = set(val.split(',')) … … 279 310 280 311 # extract the parts 281 assert(rules.has_key(' freq')) # required by RFC2445282 freq = rules[' freq'].lower()312 assert(rules.has_key('content')) # required 313 freq = rules['content'].lower() 283 314 bymonth = rules.get('bymonth') 284 315 byweekno = rules.get('byweekno') … … 335 366 336 367 # recombine the rule parts into a single string, and parse into an rruleset 337 rulestr = ';'.join(map(getXMLText, rulenode.getElementsByTagName('Rule'))) 338 ruleset = dateutil.rrule.rrulestr(rulestr, dtstart=eventdt, forceset=True) 368 # XXX FIXME: totally different rrule format 369 # rulestr = ';'.join(map(getXMLText, rulenode.getElementsByTagName('Rule'))) 370 # ruleset = dateutil.rrule.rrulestr(rulestr, dtstart=eventdt, forceset=True) 339 371 340 372 # are there any future occurrences of this event? … … 348 380 # add in the exception dates and rules (if any) 349 381 for node in exdates: 350 ruleset.exdate(dateutil.parser.parse(getXMLField(node, 'Content'))) 351 for node in exrules: 352 ruleset.exrule(dateutil.rrule.rrulestr(getXMLField(node, 'Content'))) 382 for e in getElementsByTagNames(node, 'Content'): 383 ruleset.exdate(dateutil.parser.parse(getXMLText(e))) 384 385 # XXX FIXME: totally different rrule format 386 #for node in exrules: 387 # ruleset.exrule(dateutil.rrule.rrulestr(getXMLField(node, 'Content'))) 353 388 354 389 # are there any future occurrences of this event if we consider exceptions? … … 878 913 """Returns OpenSync XML representation of this event.""" 879 914 impl = xml.dom.minidom.getDOMImplementation() 880 doc = impl.createDocument(None, 'vcal', None) 881 top = doc.createElement('Event') 882 doc.documentElement.appendChild(top) 915 doc = impl.createDocument(None, 'event', None) 916 top = doc.documentElement 883 917 884 918 e = doc.createElement('Summary') … … 891 925 else: 892 926 dtstart = self.eventdt.strftime(VCAL_DATE) 893 appendXMLChild(doc, e, 'Value', 'DATE')927 e.setAttribute('DateValue', 'DATE') 894 928 appendXMLChild(doc, e, 'Content', dtstart) 895 929 top.appendChild(e) … … 901 935 else: 902 936 dtend = endtime.strftime(VCAL_DATE) 903 appendXMLChild(doc, e, 'Value', 'DATE')937 e.setAttribute('DateValue', 'DATE') 904 938 appendXMLChild(doc, e, 'Content', dtend) 905 939 top.appendChild(e) … … 908 942 alarm = doc.createElement('Alarm') 909 943 appendXMLChild(doc, alarm, 'AlarmAction', 'DISPLAY') 910 appendXMLChild(doc, alarm, 'AlarmDescription', self.name) 944 e = doc.createElement('AlarmDescription') 945 appendXMLChild(doc, e, 'Content', self.name) 946 alarm.appendChild(e) 911 947 alarmtime = self.alarmdt.strftime(VCAL_DATETIME) 912 948 appendXMLChild(doc, alarm, 'AlarmTrigger', alarmtime) … … 915 951 e = doc.createElement('RecurrenceRule') 916 952 if self.repeat_type == MOTO_REPEAT_DAILY: 917 appendXMLChild(doc, e, ' Rule', 'FREQ=DAILY')953 appendXMLChild(doc, e, 'Content', 'DAILY') 918 954 elif self.repeat_type == MOTO_REPEAT_WEEKLY: 919 appendXMLChild(doc, e, ' Rule', 'FREQ=WEEKLY')955 appendXMLChild(doc, e, 'Content', 'WEEKLY') 920 956 elif self.repeat_type == MOTO_REPEAT_MONTHLY_DATE: 921 appendXMLChild(doc, e, ' Rule', 'FREQ=MONTHLY')922 appendXMLChild(doc, e, ' Rule', 'BYMONTHDAY=%d' % self.eventdt.day)957 appendXMLChild(doc, e, 'Content', 'MONTHLY') 958 appendXMLChild(doc, e, 'ByMonthDay', str(self.eventdt.day)) 923 959 elif self.repeat_type == MOTO_REPEAT_MONTHLY_DAY: 924 appendXMLChild(doc, e, ' Rule', 'FREQ=MONTHLY')960 appendXMLChild(doc, e, 'Content', 'MONTHLY') 925 961 day = VCAL_DAYS[self.eventdt.weekday()] 926 962 # compute the week number that the event falls in … … 929 965 else: 930 966 weeknum = self.eventdt.day / 7 + 1 931 appendXMLChild(doc, e, ' Rule', 'BYDAY=%d%s' % (weeknum, day))967 appendXMLChild(doc, e, 'ByDay', '%d%s' % (weeknum, day)) 932 968 elif self.repeat_type == MOTO_REPEAT_YEARLY: 933 appendXMLChild(doc, e, ' Rule', 'FREQ=YEARLY')934 appendXMLChild(doc, e, ' Rule', 'BYMONTH=%d' % self.eventdt.month)935 appendXMLChild(doc, e, ' Rule', 'BYMONTHDAY=%d' % self.eventdt.day)969 appendXMLChild(doc, e, 'Content', 'YEARLY') 970 appendXMLChild(doc, e, 'ByMonth', str(self.eventdt.month)) 971 appendXMLChild(doc, e, 'ByMonthDay', str(self.eventdt.day)) 936 972 937 973 if e.hasChildNodes(): … … 963 999 # work out which dates the exceptions correspond to and 964 1000 # generate ExceptionDate nodes for them 1001 e = doc.createElement('ExceptionDateTime') 1002 e.setAttribute('DateValue', 'DATE') 965 1003 for exnum in self.exceptions: 966 e = doc.createElement('ExclusionDate')967 1004 appendXMLChild(doc, e, 'Content', 968 1005 format_time(rrule[exnum], VCAL_DATE)) 969 appendXMLChild(doc, e, 'Value', 'DATE')1006 if e.hasChildNodes(): 970 1007 top.appendChild(e) 971 1008 … … 1014 1051 PhoneEvent.__init__(self) 1015 1052 doc = xml.dom.minidom.parseString(data) 1016 event = doc.getElementsByTagName(' Event')[0]1053 event = doc.getElementsByTagName('event')[0] 1017 1054 1018 1055 def getField(tagname, subtag='Content'): … … 1027 1064 raise UnsupportedDataError('Event is too old') 1028 1065 1029 durationstr = getField('Duration') 1030 if durationstr != '': 1031 self.duration = parse_ical_duration(durationstr) 1066 if event.getElementsByTagName('Duration') != []: 1067 duration = event.getElementsByTagName('Duration')[0] 1068 def tryint(s): 1069 if s == '': 1070 return 0 1071 else: 1072 return int(s) 1073 weeks = tryint(getXMLField(duration, 'Weeks')) 1074 days = tryint(getXMLField(duration, 'Days')) 1075 hours = tryint(getXMLField(duration, 'Hours')) 1076 mins = tryint(getXMLField(duration, 'Minutes')) 1077 secs = tryint(getXMLField(duration, 'Seconds')) 1078 self.duration = datetime.timedelta(weeks * 7 + days, (hours * 60 + mins) * 60 + secs) 1032 1079 else: 1033 1080 endstr = getField('DateEnd') … … 1059 1106 1060 1107 rrules = event.getElementsByTagName('RecurrenceRule') 1061 exdates = event.getElementsByTagName('Exc lusionDate')1062 exrules = event.getElementsByTagName('Exc lusionRule')1108 exdates = event.getElementsByTagName('ExceptionDateTime') 1109 exrules = event.getElementsByTagName('ExceptionRule') 1063 1110 (self.repeat_type, self.exceptions) = convert_rrule(rrules, exdates, 1064 1111 exrules, self.eventdt) … … 1256 1303 PhoneContact.__init__(self) 1257 1304 doc = xml.dom.minidom.parseString(data) 1305 contact = doc.getElementsByTagName('contact')[0] 1258 1306 1259 1307 def getField(tagname, subtag='Content'): 1260 1308 """utility function for the XML processing below""" 1261 return getXMLField( doc, tagname, subtag)1309 return getXMLField(contact, tagname, subtag) 1262 1310 1263 1311 # handle the name and formatted name fields … … 1306 1354 # filter out any illegal characters from the phone number 1307 1355 content = filter(lambda c: c in TEL_NUM_DIGITS, content) 1308 ical_types = [getXMLText(e).lower() 1309 for e in elt.getElementsByTagName('Type')] 1310 is_pref = 'pref' in ical_types 1356 ical_types = elt.getAttribute('Type').split(';') 1357 is_pref = elt.hasAttribute('Preferred') and elt.getAttribute('Preferred') in ['1', 'true'] 1311 1358 moto_type = MOTO_CONTACT_DEFAULT 1312 1359 for t in ical_types: … … 1338 1385 for adr in doc.getElementsByTagName('Address'): 1339 1386 address = [getXMLField(adr, p) for p in XML_ADDRESS_PARTS] 1340 ical_types = [getXMLText(e).lower() for e in 1341 adr.getElementsByTagName('Type')] 1387 ical_types = adr.getAttribute('Location').split(';') 1342 1388 for t in ical_types: 1343 1389 if VCARD_ADDRESS_TYPES.has_key(t): … … 1431 1477 else: 1432 1478 e = doc.createElement('Telephone') 1433 appendXMLChild(doc, e, 'Type', 1434 MOTO_CONTACT_TYPES[self.contacttype].upper()) 1479 if MOTO_PHONE_CONTACT_TYPES.has_key(self.contacttype): 1480 e.setAttribute('Type', MOTO_PHONE_CONTACT_TYPES[self.contacttype]) 1481 elif MOTO_PHONE_CONTACT_LOCATIONS.has_key(self.contacttype): 1482 e.setAttribute('Location', MOTO_PHONE_CONTACT_LOCATIONS[self.contacttype]) 1435 1483 if self.primaryflag: 1436 appendXMLChild(doc, e, 'Type', 'PREF')1484 e.setAttribute('Preferred', "1") 1437 1485 appendXMLChild(doc, e, 'Content', self.contact) 1438 1486 ret.append(e) … … 1444 1492 if e.hasChildNodes(): 1445 1493 if MOTO_ADDRESS_TYPES.has_key(self.contacttype): 1446 appendXMLChild(doc, e, 'Type',1494 e.setAttribute('Location', 1447 1495 MOTO_ADDRESS_TYPES[self.contacttype].upper()) 1448 1496 ret.append(e) … … 1584 1632 else: 1585 1633 xmldata = entry.to_xml() 1634 print xmldata 1586 1635 data = opensync.Data(xmldata, self.objformats[objtype]) 1587 1636 data.objtype = objtype … … 1752 1801 raise opensync.Error('unsupported objtype %s' % change.objtype, 1753 1802 opensync.ERROR_NOT_SUPPORTED) 1803 if change.changetype != opensync.CHANGE_TYPE_DELETED: 1804 print "\nOpenSync wants me to write:\n", change.data.data 1754 1805 if change.changetype == opensync.CHANGE_TYPE_DELETED: 1755 1806 success = (self.access.uid_seen(change.uid) … … 1805 1856 version.modelversion = str(comms.read_model()) 1806 1857 version.identifier = str(comms.read_serial()) 1807 # FIXME: discover and set capabilities1808 1858 comms.disconnect() 1809 1859 info.version = version 1860 info.capabilities = opensync.capabilities_parse(CAPABILITIES) 1810 1861 1811 1862 # for now, all objtypes are supported on all devices
