def addTaskStamp(item): ts = TaskStamp(item) ts.add() ts.summary = uw("Test Task Summary") ts.itsItem.setTriageStatus(randomEnum(pim.TriageEnum)) return ts
def parseTaskInfo(mailStamp): assert isinstance(mailStamp, MailStamp) if has_stamp(mailStamp.itsItem, TaskStamp): # The message has already been stamped as # a task return taskStamp = TaskStamp(mailStamp.itsItem) taskStamp.add()
def itemsToVObject(view, items, cal=None, filters=None): """ Iterate through items, add to cal, create a new vcalendar if needed. Consider only master events (then serialize all modifications). For now, set all timezones to Pacific. """ if filters is None: filters = () # we want filters to be iterable def makeDateTimeValue(dt, asDate=False): if asDate: return dt.date() elif dt.tzinfo == view.tzinfo.floating: return dt.replace(tzinfo=None) else: return dt def populateCommon(comp, item): """ Populate the given vevent or vtodo vobject with values for attributes common to Events or Tasks). """ if getattr(item, Note.icalUID.name, None) is None: item.icalUID = unicode(item.itsUUID) comp.add('uid').value = item.icalUID # displayName --> SUMMARY try: summary = item.displayName except AttributeError: pass else: comp.add('summary').value = summary # body --> DESCRIPTION try: description = item.body except AttributeError: pass else: if description: comp.add('description').value = description # userReminder --> VALARM if Remindable.reminders.name not in filters: firstReminder = item.getUserReminder() if firstReminder is not None: if firstReminder.absoluteTime is not None: value = firstReminder.absoluteTime else: # @@@ For now, all relative reminders are relative to starttime assert firstReminder.relativeTo == EventStamp.effectiveStartTime.name value = firstReminder.delta comp.add('valarm').add('trigger').value = value def populateCustom(comp, item): # custom properties for name, value in item.icalendarProperties.iteritems(): prop = comp.add(name) # for unrecognized properties, import stores strings, not # native types like datetimes. So value should just be a # string, not a more complicated python data structure. Don't # try to transform the value when serializing prop.isNative = False # encoding escapes characters like backslash and comma and # combines list values into a single string. This was already # done when the icalendar was imported, so don't escape again prop.encoded = True prop.value = value for name, paramstring in item.icalendarParameters.iteritems(): paramdict = comp.contents[name][0].params for paramlist in vobject.base.parseParams(paramstring): # parseParams gives a list of lists of parameters, with the # first element of each list being the name of the # parameter, followed by the parameter values, if any paramname = paramlist[0].upper() if paramname.lower() in parametersUnderstood: # parameters understood by Chandler shouldn't be stored # in icalendarParameters, but changes to which # parameters Chandler understands can lead to spurious # parameters, ignore them continue paramvalues = paramdict.setdefault(paramname, []) paramvalues.extend(paramlist[1:]) def populateEvent(comp, event): """Populate the given vobject vevent with data from event.""" populateCommon(comp, event.itsItem) try: dtstartLine = comp.add('dtstart') # allDay-ness overrides anyTime-ness if event.anyTime and not event.allDay: dtstartLine.x_osaf_anytime_param = 'TRUE' dtstartLine.value = makeDateTimeValue(event.startTime, event.anyTime or event.allDay) except AttributeError: comp.dtstart = [] # delete the dtstart that was added try: if not (event.duration == datetime.timedelta(0) or ( (event.anyTime or event.allDay) and event.duration <= oneDay)): dtendLine = comp.add('dtend') #convert Chandler's notion of allDay duration to iCalendar's if event.allDay: dtendLine.value = event.endTime.date() + oneDay else: if event.anyTime: dtendLine.x_osaf_anytime_param = 'TRUE' # anyTime should be exported as allDay for non-Chandler apps dtendLine.value = makeDateTimeValue(event.endTime, event.anyTime) except AttributeError: comp.dtend = [] # delete the dtend that was added if EventStamp.transparency.name not in filters: try: status = event.transparency.upper() # anytime events should be interpreted as not taking up time, # but all-day shouldn't if status == 'FYI' or (not event.allDay and event.anyTime): status = 'CANCELLED' comp.add('status').value = status except AttributeError: pass try: comp.add('location').value = event.location.displayName except AttributeError: pass view = event.itsItem.itsView timestamp = datetime.datetime.utcnow() comp.add('dtstamp').value = timestamp.replace(tzinfo=view.tzinfo.UTC) if event.modificationFor is not None: recurrenceid = comp.add('recurrence-id') masterEvent = event.getMaster() allDay = masterEvent.allDay or masterEvent.anyTime recurrenceid.value = makeDateTimeValue(event.recurrenceID, allDay) # logic for serializing rrules needs to move to vobject try: # hack, create RRULE line last, because it means running transformFromNative if event.getMaster() == event and event.rruleset is not None: # False because we don't want to ignore isCount for export # True because we don't want to use view.tzinfo.floating cal.vevent_list[-1].rruleset = event.createDateUtilFromRule(False, True, False) except AttributeError: logger.error('Failed to export RRULE for %s' % event.itsItem.itsUUID) # end of populateEvent function populateCustom(comp, event.itsItem) def populateModifications(event, cal): for modification in itertools.imap(EventStamp, event.modifications): for attr, val in modification.itsItem.iterModifiedAttributes(): if attr in attributesUsedWhenExporting and attr not in filters: populateEvent(cal.add('vevent'), modification) break #end helper functions def populateTask(comp, task): """Populate the given vobject vtodo with data from task.""" populateCommon(comp, task.itsItem) # @@@ [grant] Once we start writing out Event+Tasks as # VTODO, write out DUE (or maybe DTSTART) here. if Note._triageStatus.name not in filters: triageStatus = task.itsItem._triageStatus # VTODO STATUS mapping: # --------------------- # # [ICalendar] [Triage Enum] # <no value>/IN-PROCESS now (needsReply=False) # NEEDS-ACTION now (needsReply=True) # COMPLETED done # CANCELLED later if triageStatus == TriageEnum.now: if task.itsItem.needsReply: comp.add('status').value = 'needs-action' else: comp.add('status').value = 'in-process' elif triageStatus == TriageEnum.later: comp.add('status').value = 'cancelled' else: comp.add('status').value = 'completed' populateCustom(comp, task.itsItem) if cal is None: cal = vobject.iCalendar() for item in items: # main loop try: # ignore any events that aren't masters # # Note: [grant] # At the moment, we allow Event-ness to take precedence over # Task-ness. So, we serialize Event+Task objects as VEVENTs. # Part of the reason for this is that recurring VTODOs aren't # so well-supported by other iCalendar clients. For RFC 2445 # issues with VTODO+RRULE, see e.g. # <http://lists.osafoundation.org/pipermail/ietf-calsify/2006-August/001134.html> if has_stamp(item, EventStamp): event = EventStamp(item) if event.getMaster() == event: populateEvent(cal.add('vevent'), event) populateModifications(event, cal) elif has_stamp(item, TaskStamp): populateTask(cal.add('vtodo'), TaskStamp(item)) except: logger.exception("Exception while exporting %s" % item) continue return cal