def getDifference(left, right): leftIsDate = (type(left) == date) rightIsDate = (type(right) == date) if leftIsDate: if rightIsDate: return left - right else: left = forceToDateTime(view, left) elif rightIsDate: right = forceToDateTime(view, right) return makeNaiveteMatch(view, left, right.tzinfo) - right
def deserialize(cls, view, text, silentFailure=True): """ Parse an ICalendar blob into a list of record sets """ recordSets = {} extra = {'forceDateTriage': True} calname = None # iterate over calendars, usually only one, but more are allowed for calendar in vobject.readComponents(text, validate=False, ignoreUnreadable=True): if calname is None: calname = calendar.getChildValue('x_wr_calname') if calname is not None: extra['name'] = calname masters = {} for vobj in getattr(calendar, 'vevent_list', []): uid = vobj.getChildValue('uid') if vobj.getChildValue('recurrence_id') is None: masters[uid] = vobj uid_to_uuid_map = {} for vobj in chain(getattr(calendar, 'vevent_list', []), getattr(calendar, 'vtodo_list', [])): try: recurrenceID = vobj.getChildValue('recurrence_id') summary = vobj.getChildValue('summary', eim.NoChange) description = vobj.getChildValue('description', eim.NoChange) status = vobj.getChildValue('status', eim.NoChange) duration = vobj.getChildValue('duration') uid = vobj.getChildValue('uid') dtstart = vobj.getChildValue('dtstart') location = vobj.getChildValue('location', eim.NoChange) # bug 10821, Google serializes modifications with no master; # treat these as normal events, not modifications if not masters.get(uid): recurrenceID = None # can't just compare recurrenceID and dtstart, timezone could # have changed, and comparing floating to non-floating would # raise an exception if recurrenceID is None: dtstart_changed = True elif dtstart is None: dtstart_changed = False elif type(recurrenceID) == date or type(dtstart) == date: dtstart_changed = recurrenceID != dtstart else: dtstart_changed = (dtstart.tzinfo != recurrenceID.tzinfo or dtstart != recurrenceID) if status is not eim.NoChange: status = status.upper() start_obj = getattr(vobj, 'dtstart', None) isVtodo = (vobj.name == 'VTODO') osafStarred = vobj.getChildValue('x_osaf_starred') if osafStarred: osafStarred = (u"TRUE" == osafStarred.upper()) if dtstart is None or isVtodo: # due takes precedence over dtstart due = vobj.getChildValue('due') if due is not None: dtstart = due start_obj = getattr(vobj, 'due', None) anyTime = False if dtstart is not None: anyTimeParam = getattr(start_obj, 'x_osaf_anytime_param', '') anyTime = anyTimeParam.upper() == 'TRUE' isDate = type(dtstart) == date allDay = isDate and not anyTime emitEvent = (dtstart is not None) if duration is None: dtend = vobj.getChildValue('dtend') def getDifference(left, right): leftIsDate = (type(left) == date) rightIsDate = (type(right) == date) if leftIsDate: if rightIsDate: return left - right else: left = forceToDateTime(view, left) elif rightIsDate: right = forceToDateTime(view, right) return makeNaiveteMatch(view, left, right.tzinfo) - right if dtend is not None and dtstart is not None: duration = getDifference(dtend, dtstart) elif anyTime or isDate: duration = timedelta(1) else: duration = timedelta(0) # handle the special case of a midnight-to-midnight floating # event, treat it as allDay, bug 9579 if (not isDate and dtstart is not None and dtstart.tzinfo is None and dtstart.time() == midnight and duration.days >= 1 and duration == timedelta(duration.days)): allDay = True if isDate: dtstart = forceToDateTime(view, dtstart) # originally, duration was converted to Chandler's notion of # all day duration, but this step will be done by the # translator #duration -= oneDay if dtstart is not None: dtstart = convertToICUtzinfo(view, dtstart) dtstart = toICalendarDateTime(view, dtstart, allDay, anyTime) # convert to EIM value duration = toICalendarDuration(duration) uuid = UUIDFromICalUID(view, uid_to_uuid_map, uid) valarm = getattr(vobj, 'valarm', None) if valarm is not None: remValue = valarm.getChildValue('trigger') remDuration = valarm.getChildValue('duration') remRepeat = valarm.getChildValue('repeat') remDescription = valarm.getChildValue( 'description', "Event Reminder") trigger = None if remValue is not None: if type(remValue) is datetime: icutzinfoValue = convertToICUtzinfo(view, remValue) trigger = toICalendarDateTime( view, icutzinfoValue, False) else: assert type(remValue) is timedelta trigger = toICalendarDuration(remValue) if remDuration is not None: remDuration = toICalendarDuration(remDuration) if remRepeat is not None: remRepeat = int(remRepeat) recurrence = {} for rule_name in ('rrule', 'exrule'): rules = [] for line in vobj.contents.get(rule_name, []): rules.append(line.value) recurrence[rule_name] = (":".join(rules) if len(rules) > 0 else eim.NoChange) for date_name in ('rdate', 'exdate'): dates = [] for line in vobj.contents.get(date_name, []): dates.extend(line.value) if len(dates) > 0: if not (allDay or anyTime): dates = [ convertToICUtzinfo(view, dt) for dt in dates ] dt_value = toICalendarDateTime(view, dates, allDay, anyTime) else: dt_value = eim.NoChange recurrence[date_name] = dt_value if recurrenceID is not None: range = getattr(vobj.recurrence_id, 'range_param', 'THIS') if range != 'THIS': logger.info("Skipping a THISANDFUTURE or " "THISANDPRIOR modification") continue dateValue = allDay or anyTime recurrenceID = forceToDateTime(view, recurrenceID) recurrenceID = convertToICUtzinfo(view, recurrenceID) if recurrenceID.tzinfo != view.tzinfo.floating: recurrenceID = recurrenceID.astimezone(view.tzinfo.UTC) rec_string = translator.formatDateTime( view, recurrenceID, dateValue, dateValue) uuid += ":" + rec_string master = masters[uid] uid = eim.Inherit if (master.getChildValue('duration') == vobj.getChildValue( 'duration')): duration = eim.Inherit masterAnyTime = (getattr(master.dtstart, 'x_osaf_anytime_param', '') == 'TRUE') masterAllDay = (not masterAnyTime and type(master.dtstart.value) == date) if (masterAllDay == allDay and masterAnyTime == anyTime and not dtstart_changed): dtstart = eim.Inherit triage = eim.NoChange needsReply = eim.NoChange if isVtodo and status is not eim.NoChange: status = status.lower() code = vtodo_status_to_triage_code.get(status, "100") completed = vobj.getChildValue('completed') if completed is not None: if type(completed) == date: completed = TimeZone.forceToDateTime( view, completed) timestamp = str( Triageable.makeTriageStatusChangedTime( view, completed)) else: timestamp = getattr(vobj.status, 'x_osaf_changed_param', "0.0") auto = getattr(vobj.status, 'x_osaf_auto_param', 'FALSE') auto = ("1" if auto == 'TRUE' else "0") triage = code + " " + timestamp + " " + auto needsReply = (1 if status == 'needs-action' else 0) # VTODO's status doesn't correspond to EventRecord's status status = eim.NoChange icalExtra = eim.NoChange if not isVtodo: # not processing VTODOs icalExtra = extractUnrecognized(calendar, vobj) if icalExtra is None: icalExtra = '' else: icalExtra = icalExtra.serialize().decode('utf-8') records = [ model.NoteRecord( uuid, description, # body uid, # icalUid None, # icalProperties None, # icalParameters icalExtra, # icalExtra ), model.ItemRecord( uuid, summary, # title triage, # triage eim.NoChange, # createdOn eim.NoChange, # hasBeenSent (TODO) needsReply, # needsReply (TODO) eim.NoChange, # read ) ] if emitEvent: records.append( model.EventRecord( uuid, dtstart, duration, location, recurrence['rrule'], # rrule recurrence['exrule'], # exrule recurrence['rdate'], # rdate recurrence['exdate'], # exdate status, # status eim.NoChange # lastPastOccurrence )) if osafStarred: records.append(model.TaskRecord(uuid)) if valarm is not None: records.append( model.DisplayAlarmRecord(uuid, remDescription, trigger, remDuration, remRepeat)) else: records.append( model.DisplayAlarmRecord(uuid, None, None, None, None)) recordSets[uuid] = RecordSet(records) except vobject.base.VObjectError, e: icalendarLines = text.splitlines() logger.error( "Exception when importing icalendar, first 300 lines: \n%s" % "\n".join(icalendarLines[:300])) logger.exception( "import failed to import one event with exception: %s" % str(e)) if not silentFailure: raise
def deserialize(cls, view, text, silentFailure=True): """ Parse an ICalendar blob into a list of record sets """ recordSets = {} extra = {'forceDateTriage' : True} calname = None # iterate over calendars, usually only one, but more are allowed for calendar in vobject.readComponents(text, validate=False, ignoreUnreadable=True): if calname is None: calname = calendar.getChildValue('x_wr_calname') if calname is not None: extra['name'] = calname masters = {} for vobj in getattr(calendar, 'vevent_list', []): uid = vobj.getChildValue('uid') if vobj.getChildValue('recurrence_id') is None: masters[uid] = vobj uid_to_uuid_map = {} for vobj in chain( getattr(calendar, 'vevent_list', []), getattr(calendar, 'vtodo_list', []) ): try: recurrenceID = vobj.getChildValue('recurrence_id') summary = vobj.getChildValue('summary', eim.NoChange) description = vobj.getChildValue('description', eim.NoChange) status = vobj.getChildValue('status', eim.NoChange) duration = vobj.getChildValue('duration') uid = vobj.getChildValue('uid') dtstart = vobj.getChildValue('dtstart') location = vobj.getChildValue('location', eim.NoChange) # bug 10821, Google serializes modifications with no master; # treat these as normal events, not modifications if not masters.get(uid): recurrenceID = None # can't just compare recurrenceID and dtstart, timezone could # have changed, and comparing floating to non-floating would # raise an exception if recurrenceID is None: dtstart_changed = True elif dtstart is None: dtstart_changed = False elif type(recurrenceID) == date or type(dtstart) == date: dtstart_changed = recurrenceID != dtstart else: dtstart_changed = (dtstart.tzinfo != recurrenceID.tzinfo or dtstart != recurrenceID) if status is not eim.NoChange: status = status.upper() start_obj = getattr(vobj, 'dtstart', None) isVtodo = (vobj.name == 'VTODO') osafStarred = vobj.getChildValue('x_osaf_starred') if osafStarred: osafStarred = (u"TRUE" == osafStarred.upper()) if dtstart is None or isVtodo: # due takes precedence over dtstart due = vobj.getChildValue('due') if due is not None: dtstart = due start_obj = getattr(vobj, 'due', None) anyTime = False if dtstart is not None: anyTimeParam = getattr(start_obj, 'x_osaf_anytime_param', '') anyTime = anyTimeParam.upper() == 'TRUE' isDate = type(dtstart) == date allDay = isDate and not anyTime emitEvent = (dtstart is not None) if duration is None: dtend = vobj.getChildValue('dtend') def getDifference(left, right): leftIsDate = (type(left) == date) rightIsDate = (type(right) == date) if leftIsDate: if rightIsDate: return left - right else: left = forceToDateTime(view, left) elif rightIsDate: right = forceToDateTime(view, right) return makeNaiveteMatch(view, left, right.tzinfo) - right if dtend is not None and dtstart is not None: duration = getDifference(dtend, dtstart) elif anyTime or isDate: duration = timedelta(1) else: duration = timedelta(0) # handle the special case of a midnight-to-midnight floating # event, treat it as allDay, bug 9579 if (not isDate and dtstart is not None and dtstart.tzinfo is None and dtstart.time() == midnight and duration.days >= 1 and duration == timedelta(duration.days)): allDay = True if isDate: dtstart = forceToDateTime(view, dtstart) # originally, duration was converted to Chandler's notion of # all day duration, but this step will be done by the # translator #duration -= oneDay if dtstart is not None: dtstart = convertToICUtzinfo(view, dtstart) dtstart = toICalendarDateTime(view, dtstart, allDay, anyTime) # convert to EIM value duration = toICalendarDuration(duration) uuid = UUIDFromICalUID(view, uid_to_uuid_map, uid) valarm = getattr(vobj, 'valarm', None) if valarm is not None: remValue = valarm.getChildValue('trigger') remDuration = valarm.getChildValue('duration') remRepeat = valarm.getChildValue('repeat') remDescription = valarm.getChildValue('description', "Event Reminder") trigger = None if remValue is not None: if type(remValue) is datetime: icutzinfoValue = convertToICUtzinfo(view, remValue) trigger = toICalendarDateTime(view, icutzinfoValue, False) else: assert type(remValue) is timedelta trigger = toICalendarDuration(remValue) if remDuration is not None: remDuration = toICalendarDuration(remDuration) if remRepeat is not None: remRepeat = int(remRepeat) recurrence = {} for rule_name in ('rrule', 'exrule'): rules = [] for line in vobj.contents.get(rule_name, []): rules.append(line.value) recurrence[rule_name] = (":".join(rules) if len(rules) > 0 else eim.NoChange) for date_name in ('rdate', 'exdate'): dates = [] for line in vobj.contents.get(date_name, []): dates.extend(line.value) if len(dates) > 0: if not (allDay or anyTime): dates = [convertToICUtzinfo(view, dt) for dt in dates] dt_value = toICalendarDateTime(view, dates, allDay, anyTime) else: dt_value = eim.NoChange recurrence[date_name] = dt_value if recurrenceID is not None: range = getattr(vobj.recurrence_id, 'range_param', 'THIS') if range != 'THIS': logger.info("Skipping a THISANDFUTURE or " "THISANDPRIOR modification") continue dateValue = allDay or anyTime recurrenceID = forceToDateTime(view, recurrenceID) recurrenceID = convertToICUtzinfo(view, recurrenceID) if recurrenceID.tzinfo != view.tzinfo.floating: recurrenceID = recurrenceID.astimezone(view.tzinfo.UTC) rec_string = translator.formatDateTime(view, recurrenceID, dateValue, dateValue) uuid += ":" + rec_string master = masters[uid] uid = eim.Inherit if (master.getChildValue('duration') == vobj.getChildValue('duration')): duration = eim.Inherit masterAnyTime = (getattr(master.dtstart, 'x_osaf_anytime_param', '') == 'TRUE') masterAllDay = (not masterAnyTime and type(master.dtstart.value) == date) if (masterAllDay == allDay and masterAnyTime == anyTime and not dtstart_changed): dtstart = eim.Inherit triage = eim.NoChange needsReply = eim.NoChange if isVtodo and status is not eim.NoChange: status = status.lower() code = vtodo_status_to_triage_code.get(status, "100") completed = vobj.getChildValue('completed') if completed is not None: if type(completed) == date: completed = TimeZone.forceToDateTime(view, completed) timestamp = str(Triageable.makeTriageStatusChangedTime(view, completed)) else: timestamp = getattr(vobj.status, 'x_osaf_changed_param', "0.0") auto = getattr(vobj.status, 'x_osaf_auto_param', 'FALSE') auto = ("1" if auto == 'TRUE' else "0") triage = code + " " + timestamp + " " + auto needsReply = (1 if status == 'needs-action' else 0) # VTODO's status doesn't correspond to EventRecord's status status = eim.NoChange icalExtra = eim.NoChange if not isVtodo: # not processing VTODOs icalExtra = extractUnrecognized(calendar, vobj) if icalExtra is None: icalExtra = '' else: icalExtra = icalExtra.serialize().decode('utf-8') records = [model.NoteRecord(uuid, description, # body uid, # icalUid None, # icalProperties None, # icalParameters icalExtra, # icalExtra ), model.ItemRecord(uuid, summary, # title triage, # triage eim.NoChange, # createdOn eim.NoChange, # hasBeenSent (TODO) needsReply, # needsReply (TODO) eim.NoChange, # read )] if emitEvent: records.append(model.EventRecord(uuid, dtstart, duration, location, recurrence['rrule'], # rrule recurrence['exrule'], # exrule recurrence['rdate'], # rdate recurrence['exdate'], # exdate status, # status eim.NoChange # lastPastOccurrence )) if osafStarred: records.append(model.TaskRecord(uuid)) if valarm is not None: records.append( model.DisplayAlarmRecord( uuid, remDescription, trigger, remDuration, remRepeat )) else: records.append( model.DisplayAlarmRecord( uuid, None, None, None, None)) recordSets[uuid] = RecordSet(records) except vobject.base.VObjectError, e: icalendarLines = text.splitlines() logger.error("Exception when importing icalendar, first 300 lines: \n%s" % "\n".join(icalendarLines[:300])) logger.exception("import failed to import one event with exception: %s" % str(e)) if not silentFailure: raise