def task_due_date_from_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() asdate = tzutils.TaskTextToDate(xml2util.GetNodeValue(transform_ctx.current())) if tzconv.curtz() != None: # if we have a tz, we must insert the ID and convert to it dst_node.newChild(None,"TimezoneID",tzconv.curtz().name) asdate = tzconv.ConvertToLocal(asdate,tzconv.curtz()) result = asdate.strftime(DATE_FORMAT_VCALTASKLOCAL) else: # if not, does the source have a UtcStartDate element? nd = xml2util.FindChildNode(src_node.parent,"UtcDueDate") if nd != None: result = tzutils.TaskTextToDate(xml2util.GetNodeValue(nd)).strftime(DATE_FORMAT_VCALTASK) else: # we don't have this either. Better hope that the DueDate value # is correct. result = asdate.strftime(DATE_FORMAT_VCALTASKLOCAL) dst_node = transform_ctx.insertNode() dst_node.newChild(None,"Content",result) return ""
def ExceptionDateTimeFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() dst_node = transform_ctx.insertNode() exception_deleted = xml2util.GetNodeValue( xml2util.FindChildNode(src_node, "Deleted")) exception_date = xml2util.GetNodeValue( xml2util.FindChildNode(src_node, "ExceptionStartTime")) if exception_deleted != "1": raise ValueError( "Opensync does not support exceptions for modified occurrences") # We need to convert to current timezone if one is provided, else # we can assume UTC date = dateutil.TextToDate(exception_date) curtz = tzdatabase.tzdb.GetCurrentTimezone() if curtz != None: date = date.astimezone(curtz) dst_node.setProp("TimezoneID", curtz.name) dst_node = transform_ctx.insertNode() dst_node.setProp("Value", "DATE") tznode = dst_node.newChild(None, "Content", date.strftime(DATE_FORMAT_SHORT)) return ""
def TZFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) tznode = transform_ctx.current() tzdata = base64.b64decode(xml2util.GetNodeValue(tznode)) # Some AS timezones fail to include a year. See if we can # graft this from the AS body datenode = xml2util.FindChildNode(tznode.parent,"StartTime") if datenode == None: datenode = xml2util.FindChildNode(tznode.parent,"DtStamp") if datenode == None: year = datetime.datetime.now().year else: txtyear = xml2util.GetNodeValue(datenode) year = int(txtyear[0:4]) tz = tzdatabase.TZFromAirsync(tzdata,year) # only add it if we have a valid timezone if tz!=None: tzdatabase.tzdb.PutTimezone(tz) return ""
def event_reminder_to_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() content_node = xml2util.FindChildNode(src_node, "Content") value_node = xml2util.FindChildNode(src_node, "Value") related_node = xml2util.FindChildNode(src_node, "Related") if value_node == None or xml2util.GetNodeValue(value_node).lower() != "duration": return "" if related_node != None and xml2util.GetNodeValue(related_node).lower() != "start": return "" s = xml2util.GetNodeValue(content_node) s = s.lstrip("-PT") s = s.upper() days=0 hours=0 minutes=0 if s.rfind("D") != -1: days = int(s[:s.rfind("D")]) s = s[s.rfind("D")+1:] if s.rfind("H") != -1: hours = int(s[:s.rfind("H")]) s = s[s.rfind("H")+1:] if s.rfind("M") != -1: minutes = int(s[:s.rfind("M")]) s = s[s.rfind("M")+1:] return str(days * MINUTES_PER_DAY + hours * MINUTES_PER_HOUR + minutes)
def contact_position(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) curnode = transform_ctx.current() s=xml2util.GetNodeValue(curnode) # Extract the types types = [] for child in curnode.children: if child.name == "Type": types.append(xml2util.GetNodeValue(child).upper()) types.sort() position = 1 prevnode = curnode.prev while prevnode != None: if prevnode.name == curnode.name: #print "found another", curnode.name # Now compare types prev_types = [] for child in prevnode.children: if child.name == "Type": prev_types.append(xml2util.GetNodeValue(child).upper()) prev_types.sort() #print "prev types:",prev_types if prev_types == types: position += 1 prevnode = prevnode.prev return position
def contact_has_type(ctx, type_string): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) curnode = transform_ctx.current() s=xml2util.GetNodeValue(curnode) for child in curnode.children: if child.name != "Type": continue if xml2util.GetNodeValue(child).upper() == type_string: return True return False
def AttendeeFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() dst_node = transform_ctx.insertNode() email = xml2util.GetNodeValue(xml2util.FindChildNode(src_node, "Email")) name = xml2util.GetNodeValue(xml2util.FindChildNode(src_node, "Name")) if email != "": dst_node.newChild(None, "Content", "MAILTO:%s" % email) dst_node.newChild(None, "CommonName", name) return ""
def AttendeeToAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() dst_node = transform_ctx.insertNode() email = xml2util.GetNodeValue(xml2util.FindChildNode(src_node, "Content"))[7:] name = xml2util.GetNodeValue(xml2util.FindChildNode( src_node, "CommonName")) if name != "": dst_node.newChild(None, "Name", name) dst_node.newChild(None, "Email", email) return ""
def FindASDateElementContent(doc,element): up = xml2util.FindChildNode(doc,element) if up != None: return xml2util.GetNodeValue(up) else: return None
def ConvertASTimezoneToVcal(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() dst_node = transform_ctx.insertNode() s=xml2util.GetNodeValue(transform_ctx.current()) if len(s) > 0: dcas = base64.b64decode(s) base_node = transform_ctx.insertNode() astd = ASTimezoneData() astd.UnpackFromAirsync(dcas) astd.dump() sdate = FindASDateElementContent(src_node.parent,"StartTime") if sdate ==None: sdate=datetime.datetime.now() year = sdate.year else: year = int(sdate[0:4]) astd.ToVcalTZ(dst_node,year) CUR_TZ["current"] = astd.tzcurrent # We now have a (hopefully valid) timezone entry as the current # timezone. We need to back-convert the timezoneable dates as # they appear. return ""
def event_datetime_from_airsync(ctx, node=None): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) if node: node = libxml2.xmlNode(_obj=node[0]) else: node = transform_ctx.current() return tzutils.TextToDate(xml2util.GetNodeValue(node)).strftime(DATE_FORMAT_EVENT)
def ConvertDateNodeToUTC(node): date = None tdate = None udate = None ctnode = xml2util.FindChildNode(node,"Content") if ctnode != None: tdate = xml2util.GetNodeValue(ctnode).upper() if tdate != "": date = tzutils.TextToDate(tdate) udate = date if date.tzname() != "UTC": tzn=xml2util.FindChildNode(node,"TimezoneID") if tzn!=None: udate = ConvertToUTC(date,xml2util.GetNodeValue(tzn)) else: raise ValueError("Bad date content") return date,udate
def StatusToAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) curnode = transform_ctx.current() s = xml2util.GetNodeValue(curnode) if s == "COMPLETED": return "1" else: # check that PercentComplete == 100% - mark it completed if # this is the case. up = xml2util.FindChildNode(curnode.parent.parent, "PercentComplete") if up != None: ct = xml2util.FindChildNode(up, "Content") if ct != None: if xml2util.GetNodeValue(ct) == "100": return "1" return "0"
def TimeTransparencyFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) if xml2util.GetNodeValue(transform_ctx.current()) == "0": return "TRANSPARENT" else: return "OPAQUE" # 'Busy' is our default value
def event_reminder_from_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) s = int(xml2util.GetNodeValue(transform_ctx.current())) if s % MINUTES_PER_DAY == 0: return "-P%iD" % (s / MINUTES_PER_DAY) elif s % MINUTES_PER_HOUR == 0: return "-PT%iH" % (s / MINUTES_PER_HOUR) else: return "-PT%iM" % s
def AllDayEventToAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() datetext = xml2util.GetNodeValue( xml2util.FindChildNode(src_node, "Content")) if datetext.find("T") >= 0: return "0" else: return "1"
def all_description_from_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() s=xml2util.GetNodeValue(transform_ctx.current()) asnote = "" if len(s) > 0: dc = base64.b64decode(s) try: asnote = pyrtfcomp.RTFConvertToUTF8(dc,1) except pyrtfcomp.RTFException, ConvErr: pass
def ClassFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) s = xml2util.GetNodeValue(transform_ctx.current()) if s == "2": return "PRIVATE" elif s == "3": return "CONFIDENTIAL" else: return "PUBLIC" # 'PUBLIC' is our default value
def all_description_to_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() content_node = xml2util.FindChildNode(src_node, "Content") s = xml2util.GetNodeValue(content_node) ec = "" if len(s) > 0: try: asnote = pyrtfcomp.RTFConvertFromUTF8(s,RTFHDR,1) ec=base64.b64encode(asnote) except pyrtfcomp.RTFException, ConvErr: pass
def StatusFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() s = xml2util.GetNodeValue(transform_ctx.current()) if s == "1": base_node = transform_ctx.insertNode() stat_node = base_node.newChild(None, "Status", None) stat_node.newChild(None, "Content", "COMPLETED") pcnt_node = base_node.newChild(None, "PercentComplete", None) pcnt_node.newChild(None, "Content", "100") return ""
def AlarmFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() s = int(xml2util.GetNodeValue(src_node)) if s % MINUTES_PER_DAY == 0: return "-P%iD" % (s / MINUTES_PER_DAY) elif s % MINUTES_PER_HOUR == 0: return "-PT%iH" % (s / MINUTES_PER_HOUR) else: return "-PT%iM" % s
def PriorityFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) s = xml2util.GetNodeValue(transform_ctx.current()) if s == "0": return "7" elif s == "1": return "5" elif s == "2": return "3" else: return "0" # We can use the unspecced one here if we get such an one from Airsync
def OSTextToAirsyncRTF(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() s=xml2util.GetNodeValue(transform_ctx.current()) ec = "" if len(s) > 0: try: asnote = pyrtfcomp.RTFConvertFromUTF8(s,RTFHDR,1) ec=base64.b64encode(asnote) except pyrtfcomp.RTFException, ConvErr: pass
def event_starttime_from_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() dst_node = transform_ctx.insertNode() allday_node = xml2util.FindChildNode(src_node.parent, "AllDayEvent") asdate = tzutils.TextToDate(xml2util.GetNodeValue(transform_ctx.current())) if tzconv.curtz() != None: # if we have a tz, we must insert the ID and convert to it dst_node.newChild(None,"TimezoneID",tzconv.curtz().name) asdate = tzconv.ConvertToLocal(asdate,tzconv.curtz()) result = asdate.strftime(DATE_FORMAT_EVLOCAL) else: result = asdate.strftime(DATE_FORMAT_EVENT) if allday_node != None and xml2util.GetNodeValue(allday_node) != "0": result=result[0:8] dst_node.newChild(None,"Content",result) return ""
def DateFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() asdate = dateutil.TaskTextToDate(xml2util.GetNodeValue(src_node)) datetext = asdate.strftime(DATE_FORMAT_VCALTASK) dst_node = transform_ctx.insertNode() curtz = tzdatabase.tzdb.GetCurrentTimezone() if curtz != None: # if we have a tz, we must insert the ID and convert to it dst_node.setProp("TimezoneID", curtz.name) else: # if not, does the source have a UtcStartDate or UtcDueDate element? if src_node.name == "StartDate": utcdatenode = xml2util.FindChildNode(src_node.parent, "UtcStartDate") elif src_node.name == "DueDate": utcdatenode = xml2util.FindChildNode(src_node.parent, "UtcDueDate") else: utcdatenode = None if utcdatenode != None: asdate = dateutil.TaskTextToDate( xml2util.GetNodeValue(utcdatenode)) datetext = asdate.strftime(DATE_FORMAT_VCALTASK) + "Z" dst_node = transform_ctx.insertNode() dst_node.newChild(None, "Content", datetext) return ""
def PriorityToAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) # Here. let us not destroy the 'unspecified' priority when going # _to_ airsync. We can't really help reassigning this as 'low' # in the other direction. d = "0" s = xml2util.GetNodeValue(transform_ctx.current()) if s > "0": if s == "7": d = "0" elif s == "5": d = "1" elif s == "3": d = "2" else: d = "0" return d
def OSDateFromAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) tznode = transform_ctx.current() date = dateutil.TextToDate(xml2util.GetNodeValue(transform_ctx.current())) date.replace(tzinfo=tzutils.tzone.utc) # We are going from AirSync, so 'date' will be in UTC # If we have a current timezone, set it. dst_node = transform_ctx.insertNode() dst_node.setProp("Value","DATE-TIME") curtz = tzdatabase.tzdb.GetCurrentTimezone() if curtz!=None: date = date.astimezone(curtz) dst_node.setProp("TimezoneID",curtz.name) tznode=dst_node.newChild(None,"Content",date.strftime(DATE_FORMAT_EVENT)) return ""
def AlarmToAirsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() desc_node = xml2util.FindChildNode(src_node,"Description") content_node = xml2util.FindChildNode(src_node, "AlarmTrigger") # value attribute is mandatory. Convert to blank string if not # present. We only handle 'DURATION' values here, DATE-TIME is a # TODO (we can convert this into a duration for AS) value_node = xml2util.GetNodeAttr(src_node,"Value") if value_node == None: return "" if value_node.lower() != "duration": return "" s = xml2util.GetNodeValue(content_node).lstrip("-PT").upper() days=0 hours=0 minutes=0 if s.rfind("D") != -1: days = int(s[:s.rfind("D")]) s = s[s.rfind("D")+1:] if s.rfind("H") != -1: hours = int(s[:s.rfind("H")]) s = s[s.rfind("H")+1:] if s.rfind("M") != -1: minutes = int(s[:s.rfind("M")]) s = s[s.rfind("M")+1:] return str(days * MINUTES_PER_DAY + hours * MINUTES_PER_HOUR + minutes)
def event_recurrence_from_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() dst_node = transform_ctx.insertNode() interval_node = xml2util.FindChildNode(src_node, "Interval") until_node = xml2util.FindChildNode(src_node, "Until") occurrences_node = xml2util.FindChildNode(src_node, "Occurrences") type_node = xml2util.FindChildNode(src_node, "Type") dayofweek_node = xml2util.FindChildNode(src_node, "DayOfWeek") dayofmonth_node = xml2util.FindChildNode(src_node, "DayOfMonth") weekofmonth_node = xml2util.FindChildNode(src_node, "WeekOfMonth") monthofyear_node = xml2util.FindChildNode(src_node, "MonthOfYear") # Add the common nodes that don't really require conversion if interval_node != None: dst_node.newChild(None, "Rule", "INTERVAL=%s" % xml2util.GetNodeValue(interval_node)) if until_node != None: dst_node.newChild(None, "Rule", "UNTIL=%s" % xml2util.GetNodeValue(until_node)) if occurrences_node != None: dst_node.newChild(None, "Rule", "COUNT=%s" % xml2util.GetNodeValue(occurrences_node)) if type_node != None: type = int(xml2util.GetNodeValue(type_node)) # Special case: we can treat this as simple weekly event if type == 0 and dayofweek_node != None: type = 1 if type == 0: dst_node.newChild(None, "Rule", "FREQ=DAILY") elif type == 1: dst_node.newChild(None, "Rule", "FREQ=WEEKLY") dst_node.newChild(None, "Rule", "BYDAY=%s" % airsync_days_to_vcal_days(xml2util.GetNodeValue(dayofweek_node))) elif type == 2: dst_node.newChild(None, "Rule", "FREQ=MONTHLY") dst_node.newChild(None, "Rule", "BYMONTHDAY=%s" % xml2util.GetNodeValue(dayofmonth_node)) elif type == 3: dst_node.newChild(None, "Rule", "FREQ=MONTHLY") dst_node.newChild(None, "Rule", "BYDAY=%s" % generate_vcal_byday(xml2util.GetNodeValue(weekofmonth_node), xml2util.GetNodeValue(dayofweek_node))) elif type == 5: dst_node.newChild(None, "Rule", "FREQ=YEARLY") dst_node.newChild(None, "Rule", "BYMONTH=%s" % xml2util.GetNodeValue(monthofyear_node)) dst_node.newChild(None, "Rule", "BYMONTHDAY=%s" % xml2util.GetNodeValue(dayofmonth_node)) elif type == 6: dst_node.newChild(None, "Rule", "FREQ=YEARLY") dst_node.newChild(None, "Rule", "BYMONTH=%s" % xml2util.GetNodeValue(monthofyear_node)) dst_node.newChild(None, "Rule", "BYDAY=%s" % generate_vcal_byday(xml2util.GetNodeValue(weekofmonth_node), xml2util.GetNodeValue(dayofweek_node))) else: # Unsupported type raise ValueError("Unknown recurrence type %d from Airsync" % type) else: # If we don't know what type of recurrence it is, we # can't construct its vcal rules raise ValueError("No recurrence type specified from Airsync") return ""
def event_recurrence_to_airsync(ctx): parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx) src_node = transform_ctx.current() dst_node = transform_ctx.insertNode() # Extract the rules src_rules = {} child = src_node.children while child != None: if child.name == "Rule": rrule_val = xml2util.GetNodeValue(child) sep = rrule_val.index("=") key = rrule_val[:sep] val = rrule_val[sep+1:] src_rules[key.lower()] = val.lower() child = child.next # Interval, Count, and Until rules have straightforward conversions if src_rules.has_key("interval"): dst_node.newChild(None, "Interval", src_rules["interval"]) if src_rules.has_key("until"): dst_node.newChild(None, "Until", tzutils.TextToDate(src_rules["until"]).strftime(DATE_FORMAT_EVENT)) if src_rules.has_key("count"): dst_node.newChild(None, "Occurrences", src_rules["count"]) # Handle different types of recurrences on a case-by-case basis if src_rules["freq"].lower() == "daily": # There really isn't much to convert in this case.. dst_node.newChild(None, "Type", "0") elif src_rules["freq"].lower() == "weekly": dst_node.newChild(None, "Type", "1") dst_node.newChild(None, "DayOfWeek", str(vcal_days_to_airsync_days(src_rules["byday"]))) elif src_rules["freq"].lower() == "monthly": if src_rules.has_key("bymonthday"): dst_node.newChild(None, "Type", "2") dst_node.newChild(None, "DayOfMonth", src_rules["bymonthday"]) elif src_rules.has_key("byday"): week, day = vcal_split_byday(src_rules["byday"]) dst_node.newChild(None, "Type", "3") dst_node.newChild(None, "DayOfWeek", str(vcal_days_to_airsync_days(day))) if week >= 0: dst_node.newChild(None, "WeekOfMonth", week) elif week == -1: # Airsync deals with this as a special case dst_node.newChild(None, "WeekOfMonth", "5") else: # Not supported (as far as I can tell...) # Airsync cannot count backwards from the end of the # month in general raise ValueError("Airsync does not support counting from end of month") else: # It seems like this should be against the rules, and filling in # a default might not make sense because either of the above interpretations # is equally valid. raise ValueError("Monthly events must either specify BYMONTHDAY or BYDAY rules") elif src_rules["freq"].lower() == "yearly": if src_rules.has_key("bymonth"): if src_rules.has_key("bymonthday"): dst_node.newChild(None, "Type", "5") dst_node.newChild(None, "MonthOfYear", src_rules["bymonth"]) dst_node.newChild(None, "DayOfMonth", src_rules["bymonthday"]) elif src_rules.has_key("byday"): week, day = vcal_split_byday(src_rules["byday"]) dst_node.newChild(None, "Type", "6") dst_node.newChild(None, "MonthOfYear", src_rules["bymonth"]) dst_node.newChild(None, "DayOfWeek", str(vcal_days_to_airsync_days(day))) if week >= 0: dst_node.newChild(None, "WeekOfMonth", week) elif week == -1: # Airsync deals with this as a special case dst_node.newChild(None, "WeekOfMonth", "5") else: # Not supported (as far as I can tell...) # Airsync cannot count backwards from the end of the # month in general raise ValueError("Airsync does not support counting from end of month") else: # It seems like this should be against the rules, and filling in # a default might not make sense because either of the above interpretations # is equally valid. raise ValueError("Yearly events which are by month must either specify BYMONTHDAY or BYDAY rules") elif src_rules.has_key("byyearday"): # Not supported (as far as I can tell...) # Airsync does not have a 'DayOfYear' field raise ValueError("Airsync does not support day-of-year yearly events") else: # we need to get the start/recurrence date from the start date # Get the start date - we need this to expand yearly rules # We should always have a start date in a legal event! start = xml2util.FindChildNode(src_node.parent,"DateStarted") utcdate=tzconv.ConvertDateNodeToUTC(start)[1] # We need the month and the day dst_node.newChild(None,"Type", "5") dst_node.newChild(None,"MonthOfYear",str(utcdate.month)) dst_node.newChild(None,"DayOfMonth",str(utcdate.day)) return ""