def _makeRecurrenceRuleSet(self, until=None, freq='daily'):
     ruleItem = RecurrenceRule(None, itsParent=self.sandbox)
     ruleItem.freq = freq
     if until is not None:
         ruleItem.until = until
     ruleSetItem = RecurrenceRuleSet(None, itsParent=self.sandbox)
     ruleSetItem.addRule(ruleItem)
     return ruleSetItem
 def _makeRecurrenceRuleSet(self, until=None, freq='daily'):
     ruleItem = RecurrenceRule(None, itsParent=self.sandbox)
     ruleItem.freq = freq
     if until is not None:
         ruleItem.until = until
     ruleSetItem = RecurrenceRuleSet(None, itsParent=self.sandbox)
     ruleSetItem.addRule(ruleItem)
     return ruleSetItem
 def testExDate(self):
     ruleSet = dateutil.rrule.rruleset()
     for freq in 'weekly', 'monthly':
         ruleSet.rrule(self._createBasicDateUtil(freq))
     ruleSet.exdate(self.start)
     ruleSetItem = RecurrenceRuleSet(None, itsView=self.view)
     ruleSetItem.setRuleFromDateUtil(ruleSet)
     identityTransformed = ruleSetItem.createDateUtilFromRule(self.start)
     self.assertNotEqual(self.start, identityTransformed[0])
Beispiel #4
0
 def testExDate(self):
     ruleSet = dateutil.rrule.rruleset()
     for freq in 'weekly', 'monthly':
         ruleSet.rrule(self._createBasicDateUtil(freq))
     ruleSet.exdate(self.start)
     ruleSetItem = RecurrenceRuleSet(None, view=self.rep.view)
     ruleSetItem.setRuleFromDateUtil(ruleSet)
     identityTransformed = ruleSetItem.createDateUtilFromRule(self.start)
     self.assertNotEqual(self.start, identityTransformed[0])
Beispiel #5
0
 def _makeRecurrenceRuleSet(self, until=None, freq='daily', byweekday=None):
     ruleItem = RecurrenceRule(None, itsView=self.view)
     ruleItem.freq = freq
     if byweekday is not None:
         ruleItem.byweekday = byweekday
     if until is not None:
         ruleItem.until = until
     ruleSetItem = RecurrenceRuleSet(None, itsView=self.view)
     ruleSetItem.addRule(ruleItem)
     return ruleSetItem
Beispiel #6
0
 def _makeRecurrenceRuleSet(self, until=None, freq='daily', byweekday=None):
     ruleItem = RecurrenceRule(None, itsView=self.view)
     ruleItem.freq = freq
     if byweekday is not None:
         ruleItem.byweekday = byweekday
     if until is not None:
         ruleItem.until = until
     ruleSetItem = RecurrenceRuleSet(None, itsView=self.view)
     ruleSetItem.addRule(ruleItem)
     return ruleSetItem
Beispiel #7
0
 def _createRuleSetItem(self, freq):
     ruleItem = RecurrenceRule(None, view=self.rep.view)
     ruleItem.until = getattr(self, freq)['end']
     ruleItem.untilIsDate = False
     if freq == 'weekly':
         self.assertEqual(ruleItem.freq, 'weekly',
                          "freq should default to weekly")
     else:
         ruleItem.freq = freq
     ruleSetItem = RecurrenceRuleSet(None, view=self.rep.view)
     ruleSetItem.addRule(ruleItem)
     return ruleSetItem
 def _createRuleSetItem(self, freq):
     ruleItem = RecurrenceRule(None, view=self.rep.view)
     ruleItem.until = getattr(self, freq)['end']
     ruleItem.untilIsDate = False
     if freq == 'weekly':
         self.assertEqual(ruleItem.freq, 'weekly', 
                          "freq should default to weekly")
     else:
         ruleItem.freq = freq
     ruleSetItem = RecurrenceRuleSet(None, view=self.rep.view)
     ruleSetItem.addRule(ruleItem)
     return ruleSetItem
Beispiel #9
0
    def testExRule(self):
        ruleSet = dateutil.rrule.rruleset()
        for freq in 'weekly', 'monthly':
            ruleSet.rrule(self._createBasicDateUtil(freq))
        exrule = dateutil.rrule.rrule(WEEKLY, count=10, dtstart=self.start)
        ruleSet.exrule(exrule)

        ruleSetItem = RecurrenceRuleSet(None, view=self.rep.view)
        ruleSetItem.setRuleFromDateUtil(ruleSet)
        identityTransformed = ruleSetItem.createDateUtilFromRule(self.start)
        # The monthly rule dates aren't in the exclusion rule
        self.assertEqual(identityTransformed[0],
                         self.start + timedelta(days=31))
        self.assertEqual(identityTransformed.count(),
                         self.weekly['count'] + self.monthly['count'] - 1 - 10)
    def testExRule(self):
        ruleSet = dateutil.rrule.rruleset()
        for freq in 'weekly', 'monthly':
            ruleSet.rrule(self._createBasicDateUtil(freq))
        exrule = dateutil.rrule.rrule(WEEKLY, count=10, dtstart=self.start)
        ruleSet.exrule(exrule)

        ruleSetItem = RecurrenceRuleSet(None, itsView=self.view)
        ruleSetItem.setRuleFromDateUtil(ruleSet)
        identityTransformed = ruleSetItem.createDateUtilFromRule(self.start)
        # The monthly rule dates aren't in the exclusion rule
        self.assertEqual(identityTransformed[0],self.start + timedelta(days=31))
        self.assertEqual(identityTransformed.count(), self.weekly['count'] +
                                                      self.monthly['count'] - 1
                                                      - 10)
    def testRDate(self):
        ruleSet = dateutil.rrule.rruleset()
        for freq in 'weekly', 'monthly':
            ruleSet.rrule(self._createBasicDateUtil(freq))
        ruleSet.rdate(self.start + timedelta(days=1))
        ruleSet.rdate(self.start + timedelta(days=2))
        ruleSetItem = RecurrenceRuleSet(None, itsView=self.view)
        ruleSetItem.setRuleFromDateUtil(ruleSet)

        self.assertEqual(ruleSetItem.rdates[0], self.start + timedelta(days=1))

        identityTransformed = ruleSetItem.createDateUtilFromRule(self.start)
        self.assertEqual(identityTransformed[2], self.start + timedelta(days=2))
        self.assertEqual(identityTransformed.count(), self.weekly['count'] +
                                                      self.monthly['count'] - 1
                                                      + 2)
Beispiel #12
0
    def testRDate(self):
        ruleSet = dateutil.rrule.rruleset()
        for freq in 'weekly', 'monthly':
            ruleSet.rrule(self._createBasicDateUtil(freq))
        ruleSet.rdate(self.start + timedelta(days=1))
        ruleSet.rdate(self.start + timedelta(days=2))
        ruleSetItem = RecurrenceRuleSet(None, view=self.rep.view)
        ruleSetItem.setRuleFromDateUtil(ruleSet)

        self.assertEqual(ruleSetItem.rdates[0], self.start + timedelta(days=1))

        identityTransformed = ruleSetItem.createDateUtilFromRule(self.start)
        self.assertEqual(identityTransformed[2],
                         self.start + timedelta(days=2))
        self.assertEqual(identityTransformed.count(),
                         self.weekly['count'] + self.monthly['count'] - 1 + 2)
Beispiel #13
0
def addEventStamp(item, recur=False):
    es = EventStamp(item)
    es.add()
    es.summary = uw("Test Event Summary")

    tzinfo = item.itsView.tzinfo.floating

    # Choose random days, hours
    startDelta = timedelta(days=random.randint(0, 30),
                           hours=random.randint(0, 24))

    now = datetime.now(tzinfo)

    closeToNow = datetime(now.year,
                          now.month,
                          now.day,
                          now.hour,
                          int(now.minute / 30) * 30,
                          tzinfo=now.tzinfo)

    es.startTime = closeToNow + startDelta
    es.anyTime = True

    # Choose random minutes
    es.duration = timedelta(minutes=60)

    es.location = Calendar.Location.getLocation(view, uw("My House"))

    es.itsItem.importance = random.choice(pim.ImportanceEnum.values)

    es.itsItem.setTriageStatus(randomEnum(pim.TriageEnum))

    if recur:
        rule = RecurrenceRule(itsView=view)
        rule.freq = 'daily'
        rule.until = datetime(2008, 9, 14, 19, tzinfo=view.tzinfo.default)
        rule.untilIsDate = False

        ruleSet = RecurrenceRuleSet(itsView=view)
        ruleSet.addRule(rule)

        es.rruleset = ruleSet

    return es
Beispiel #14
0
    def testTwoRuleSet(self):
        """Test two RecurrenceRules composed into a RuleSet."""
        ruleSetItem = RecurrenceRuleSet(None, view=self.rep.view)
        ruleItem = self._createBasicItem('weekly')
        ruleSetItem.addRule(ruleItem)
        ruleSet = ruleSetItem.createDateUtilFromRule(self.start)

        #rrulesets support the rrule interface
        self._testRRule('weekly', ruleSet)

        ruleItem = self._createBasicItem('monthly')
        ruleSetItem.addRule(ruleItem)
        self._testCombined(ruleSetItem.createDateUtilFromRule(self.start))
Beispiel #15
0
def addEventStamp(item, recur=False):
    es = EventStamp(item)
    es.add()
    es.summary = uw("Test Event Summary")

    tzinfo = view.tzinfo.floating

    # Choose random days, hours
    startDelta = timedelta(days=random.randint(0, 30),
                           hours=random.randint(0, 24))

    now = datetime.now(tzinfo)

    closeToNow = datetime(now.year, now.month, now.day, now.hour,
                          int(now.minute/30) * 30, tzinfo=now.tzinfo)

    es.startTime = closeToNow + startDelta
    es.anyTime = True

    # Choose random minutes
    es.duration = timedelta(minutes=60)

    es.location = Calendar.Location.getLocation(view, uw("My House"))

    es.itsItem.importance = random.choice(pim.ImportanceEnum.values)

    es.itsItem.setTriageStatus(randomEnum(pim.TriageEnum))

    if recur:
        rule = RecurrenceRule(itsView=view)
        rule.freq = 'daily'
        rule.until =  datetime(2008, 9, 14, 19, tzinfo=view.tzinfo.default)
        rule.untilIsDate = False

        ruleSet = RecurrenceRuleSet(itsView=view)
        ruleSet.addRule(rule)

        es.rruleset = ruleSet

    return es
Beispiel #16
0
    def testRuleSetFromDateUtil(self):
        ruleSet = dateutil.rrule.rruleset()
        for freq in 'weekly', 'monthly':
            ruleSet.rrule(self._createBasicDateUtil(freq))
        ruleSetItem = RecurrenceRuleSet(None, view=self.rep.view)
        ruleSetItem.setRuleFromDateUtil(ruleSet)
        self._testCombined(ruleSetItem.createDateUtilFromRule(self.start))

        # test setting a rule instead of a ruleset
        ruleSetItem.setRuleFromDateUtil(self._createBasicDateUtil('weekly'))
        self._testRRule('weekly',
                        ruleSetItem.createDateUtilFromRule(self.start))

        # test raising an exception when setting a non-rrule or rruleset
        self.assertRaises(TypeError, ruleSetItem.setRuleFromDateUtil, 0)
    def testTwoRuleSet(self):
        """Test two RecurrenceRules composed into a RuleSet."""
        ruleSetItem = RecurrenceRuleSet(None, itsView=self.view)
        ruleItem = self._createBasicItem('weekly')
        ruleSetItem.addRule(ruleItem)
        ruleSet = ruleSetItem.createDateUtilFromRule(self.start)

        #rrulesets support the rrule interface
        self._testRRule('weekly', ruleSet)

        ruleItem = self._createBasicItem('monthly')
        ruleSetItem.addRule(ruleItem)
        self._testCombined(ruleSetItem.createDateUtilFromRule(self.start))
    def testRuleSetFromDateUtil(self):
        ruleSet = dateutil.rrule.rruleset()
        for freq in 'weekly', 'monthly':
            ruleSet.rrule(self._createBasicDateUtil(freq))
        ruleSetItem = RecurrenceRuleSet(None, itsView=self.view)
        ruleSetItem.setRuleFromDateUtil(ruleSet)
        self._testCombined(ruleSetItem.createDateUtilFromRule(self.start))

        # test setting a rule instead of a ruleset
        ruleSetItem.setRuleFromDateUtil(self._createBasicDateUtil('weekly'))
        self._testRRule('weekly',ruleSetItem.createDateUtilFromRule(self.start))

        # test raising an exception when setting a non-rrule or rruleset
        self.assertRaises(TypeError, ruleSetItem.setRuleFromDateUtil, 0)
def GenerateCalendarEvent(view, args):
    """ Generate one calendarEvent item """
    event = Calendar.CalendarEvent(itsView=view)

    # displayName
    if args[0]=='*': # semi-random data
        event.displayName = random.choice(HEADLINES)

    elif not args[0]=='':
        event.displayName = u"%s" %args[0]
    else:
        event.displayName = u'untitled'

    if TEST_I18N:
        event.displayName = uw(event.displayName)

    #startTime (startDate + startTime) + TimeZone
    event.startTime = ReturnCompleteDatetime(view, args[2], args[3],
                                             tz=args[12])
   
    #anyTime
    if args[4]=='*': # semi-random data
        r = random.randint(0,100)
        if r < 95: # 95% chance that we'll turn anyTime off
            event.anyTime = False
        else:
            event.anyTime = True
    elif args[4]=='TRUE' :
        event.anyTime = True
    else:
        event.anyTime = False

    #allDay    
    if args[5]=='*': # semi-random data
        r = random.randint(0,100)
        if r < 5: # 5% chance of allDay
            event.allDay = True
        else:
            event.allDay = False
    elif args[5]=='TRUE' :
        event.allDay = True
    else:
        event.allDay = False

    #duration
    if args[6]=='*': # semi-random data
        event.duration = timedelta(minutes=random.choice(DURATIONS))
    elif not args[6]=='':
        event.duration = timedelta(minutes=string.atoi(args[6]))
    else:
        event.duration = timedelta(minutes=60) #default value 1h

    #reminders
    if not args[7]=='':
        if args[7]=='*': # semi-random data
            reminderInterval = random.choice(REMINDERS)
        else:
            reminderInterval = string.atoi(args[7])
        event.userReminderInterval = timedelta(minutes=-reminderInterval)

    #location
    if args[8]=='*': # semi-random data
        event.location = Calendar.Location.getLocation(view, random.choice(LOCATIONS))

    elif not args[8]=='':
        event.location = Calendar.Location.getLocation(view,u"%s"%args[8])    

    if TEST_I18N:
        event.location = uw(event.location)

    #status (only 3 values allowed : 'Confirmed','Tentative','fyi')
    if args[9]=='*': # semi-random data
        event.transparency = random.choice(STATUS)
    elif string.lower(args[9]) in STATUS:    
        event.transparency = string.lower(args[9])
    else: # default value (normal)
        event.transparency = 'confirmed'
    
    #recurrence ('daily','weekly','monthly','yearly') + recurrence end date
    ruleItem = RecurrenceRule(None, itsView=view)
    ruleSetItem = RecurrenceRuleSet(None, itsView=view)
    if not args[11] == '':
        ruleItem.until = ReturnCompleteDatetime(view, args[11])
    if args[10]=='*': # semi-random data
        ruleItem.freq = random.choice(RECURRENCES)
        ruleSetItem.addRule(ruleItem)
        event.rruleset = ruleSetItem
    elif string.lower(args[10]) in RECURRENCES:
        ruleItem.freq = string.lower(args[10])
        ruleSetItem.addRule(ruleItem)
        event.rruleset = ruleSetItem

    #collection
    if args[1]=='*': # semi-random data
        if not len(collectionsDict) == 0:
            collectionsDict.values()[random.randint(0,len(collectionsDict)-1)].add(event)
    elif not args[1]=='':
        collectionNames = string.split(args[1], ';')
        for name in collectionNames:
            if collectionsDict.has_key(name):
                collectionsDict[name].add(event)
            else:
                GenerateCollection(view, [name])
                collectionsDict[name].add(event)

    return event
Beispiel #20
0
def createItems(paramDict):

    if bool(wx.GetApp()):
        view = QAUITestAppLib.App_ns.itsView
    else:  # when running unit tests there is no app
        view = NullRepositoryView(verify=True)
    tzinfo = view.tzinfo.getDefault()

    totalItems = int(paramDict['textCtrlTotalItems'])

    #get titles to use
    TITLES = getDataFromFile(paramDict['textCtrlTitleSourceFile'])

    #create collections to use
    collectionNames = addRandomWithoutDups(
        getDataFromFile(paramDict['textCtrlCollectionFileName']),
        int(paramDict['textCtrlCollectionCount']))
    sidebarCollections = createCollections(collectionNames, view)

    #determine how many collections each item should participate in
    collectionMembershipIndex = createMembershipIndex(
        paramDict['textCtrlCollectionMembership'], range(totalItems))
    collectionMembershipDict = {}
    ##fill collectionMembershipDict with correct number of collections for each item index
    for i in range(totalItems):
        collectionMembershipDict[i] = addRandomWithoutDups(
            sidebarCollections, collectionMembershipIndex[i])

    #get locations to use
    LOCATIONS = getDataFromFile(paramDict['textCtrlLocationSourceFilePath'])

    #get note field text
    NOTES = getDataFromFile(paramDict['textCtrlNoteSourceFilePath'], note=True)

    itemTypes = percentsToCount(
        totalItems, {
            'mail': int(paramDict['choicePercentMail']),
            'task': int(paramDict['choicePercentTask']),
            'event': int(paramDict['choicePercentEvent'])
        })

    triageStatus = percentsToCount(
        totalItems, {
            'unassigned': int(paramDict['choicePercentUnassignedStatus']),
            'now': int(paramDict['choicePercentNow']),
            'later': int(paramDict['choicePercentLater']),
            'done': int(paramDict['choicePercentDone'])
        },
        randomize=True)
    triageStatusAssignments = percentsToAssignments(triageStatus)
    #convert the string triage value to the triageEnum that can be used to set it
    triageEnums = {
        'now': pim.TriageEnum.now,
        'later': pim.TriageEnum.later,
        'done': pim.TriageEnum.done
    }
    triageStatusUnassigned = []
    for index, status in triageStatusAssignments.iteritems():
        if status == 'unassigned':
            triageStatusUnassigned.append(index)
        else:
            triageStatusAssignments[index] = triageEnums[status]

    recurTypes = percentsToCount(
        itemTypes['event'], {
            'non': int(paramDict['choicePercentNonRecurring']),
            'daily': int(paramDict['choicePercentDaily']),
            'weekly': int(paramDict['choicePercentWeekly']),
            'biweekly': int(paramDict['choicePercentBiWeekly']),
            'monthly': int(paramDict['choicePercentMonthly']),
            'yearly': int(paramDict['choicePercentYearly'])
        },
        randomize=True)

    durationTypes = percentsToCount(
        itemTypes['event'], {
            'allDay': int(paramDict['choicePercentAllDay']),
            'atTime': int(paramDict['choicePercentAtTime']),
            'anyTime': int(paramDict['choicePercentAnyTime']),
            'duration': int(paramDict['choicePercentDuration'])
        },
        randomize=True)

    eventStatus = percentsToCount(
        durationTypes['duration'], {
            'confirmed': int(paramDict['choicePercentConfirmed']),
            'FYI': int(paramDict['choicePercentFYI']),
            'tentative': int(paramDict['choicePercentTentative'])
        },
        randomize=True)
    eventStatusAssignment = percentsToAssignments(eventStatus)

    global startTimes
    startTimes = createStartTimeRange(paramDict['textCtrlTimeOfDay'],
                                      itemTypes['event'])

    startDateText = paramDict['textCtrlStartDate']
    if startDateText:
        y, m, d = startDateText.split(',')
        startDate = datetime(int(y), int(m), int(d))
        y, m, d = paramDict['textCtrlEndDate'].split(',')
        endDate = datetime(int(y), int(m), int(d))
        global dates
        dates = createDateRange(startDate, endDate)

    durationsTimed = createDurationIndex(paramDict['textCtrlDuration'],
                                         durationTypes['duration'])
    itemsWithDuration = durationsTimed.keys()

    relativeReminders, absoluteReminders = createAlarmIndex(
        paramDict['textCtrlAlarmSpec'], itemTypes['event'], totalItems, tzinfo)

    allRecurring = recurTypes['daily'] + recurTypes['weekly'] + recurTypes[
        'biweekly'] + recurTypes['monthly'] + recurTypes['yearly']
    recurenceEndDate = createEndDateIndex(
        paramDict['textCtrlRecurrenceEndDates'], allRecurring)

    # email stuff
    emailAddresses = []
    for firstName in firstNames:
        for lastName in lastNames:
            for domain in domainList:
                emailAddresses.append(firstName + lastName + '@' + domain)
    # get address sources
    if paramDict['textCtrlToFile'] == '':
        toAddresses = emailAddresses
    else:
        toAddresses = getDataFromFile(paramDict['textCtrlToFile'])

    if paramDict['textCtrlCCFileName'] == '':
        ccAddresses = emailAddresses
    else:
        ccAddresses = getDataFromFile(paramDict['textCtrlCCFileName'])

    if paramDict['textCtrlBCCFileName'] == '':
        bccAddresses = emailAddresses
    else:
        bccAddresses = getDataFromFile(paramDict['textCtrlBCCFileName'])

    # make email indexes from specs
    toIndex = createAddressIndex(itemTypes['mail'],
                                 paramDict['textCtrlToSpec'], toAddresses)
    ccIndex = createAddressIndex(itemTypes['mail'],
                                 paramDict['textCtrlCCSpec'], ccAddresses)
    bccIndex = createAddressIndex(itemTypes['mail'],
                                  paramDict['textCtrlBCCSpec'], bccAddresses)

    created = []
    for i in range(totalItems):
        created.append(pim.notes.Note(itsView=view))
        item = created[i]
        item.displayName = random.choice(TITLES)
        item.body = random.choice(NOTES)
        if i not in triageStatusUnassigned:
            item.setTriageStatus(triageStatusAssignments[i])
        if i in absoluteReminders.keys():
            item.setUserReminderTime(
                datetime.combine(date.today(), absoluteReminders[i]))
        if i in itemTypes['mail']:
            mailStampedItem = pim.MailStamp(item)
            mailStampedItem.add()
            if i in toIndex.keys():
                for address in toIndex[i]:
                    mailStampedItem.toAddress.append(
                        Mail.EmailAddress.getEmailAddress(view, address))
            if i in ccIndex.keys():
                for address in ccIndex[i]:
                    mailStampedItem.ccAddress.append(
                        Mail.EmailAddress.getEmailAddress(view, address))
            if i in bccIndex.keys():
                for address in bccIndex[i]:
                    mailStampedItem.bccAddress.append(
                        Mail.EmailAddress.getEmailAddress(view, address))
        if i in itemTypes['task']: pim.TaskStamp(item).add()
        if i in itemTypes['event']:
            eventStampedItem = pim.EventStamp(item)
            eventStampedItem.add()
            if i in itemsWithDuration:  #if its an event with duration then assign status
                eventStampedItem.transparency = eventStatusAssignment[i].lower(
                )
            eventStampedItem.location = Calendar.Location.getLocation(
                view, random.choice(LOCATIONS))
            eventStampedItem.anyTime = i in durationTypes['anyTime']
            eventStampedItem.allDay = i in durationTypes['allDay']
            eventStampedItem.startTime = createStartTime(i, tzinfo)
            if i in durationTypes['atTime']:
                eventStampedItem.duration = timedelta(0)
            else:
                if i in itemsWithDuration:
                    eventStampedItem.duration = durationsTimed[i]
            if i in relativeReminders.keys():
                eventStampedItem.setUserReminderInterval(relativeReminders[i])
            if i in allRecurring:
                ruleItem = RecurrenceRule(None, itsView=view)
                ruleSetItem = RecurrenceRuleSet(None, itsView=view)
                recurFreq = rangeName(i, recurTypes)
                #specail case biweekly
                if recurFreq == 'biweekly':
                    ruleItem.freq = 'weekly'
                    ruleItem.interval = 2
                else:
                    ruleItem.freq = recurFreq
                #add end date
                if i in recurenceEndDate and recurenceEndDate[
                        i]:  # zero means don't use an end date
                    endDateDate = calcEndDate(eventStampedItem.startTime,
                                              recurenceEndDate[i], recurFreq)
                    ruleItem.until = endDateDate
                ruleSetItem.addRule(ruleItem)
                eventStampedItem.rruleset = ruleSetItem
        for collection in collectionMembershipDict[i]:
            collection.add(item)
    return created  #return value used for testing in TestCreateItems.py:TestItemCreation
    def testChange(self):
        from osaf import sharing
        from osaf.pim import ListCollection

        self.tzprefs.showUI = False

        gayParee = self.view.tzinfo.getInstance("Europe/Paris")

        master = CalendarEvent(itsView=self.view,
                               anyTime=False,
                               startTime=datetime(
                                   2007,
                                   2,
                                   7,
                                   11,
                                   30,
                                   tzinfo=self.view.tzinfo.floating),
                               duration=timedelta(hours=1))

        master.rruleset = RecurrenceRuleSet(
            itsView=self.view,
            rrules=[RecurrenceRule(itsView=self.view, freq='daily')])

        ordinary = CalendarEvent(itsView=self.view,
                                 anyTime=False,
                                 startTime=datetime(
                                     2007,
                                     2,
                                     7,
                                     11,
                                     30,
                                     tzinfo=self.view.tzinfo.floating),
                                 duration=timedelta(hours=1))

        sharedFloating = CalendarEvent(itsView=self.view,
                                       anyTime=False,
                                       startTime=datetime(
                                           2002,
                                           12,
                                           22,
                                           tzinfo=self.view.tzinfo.floating))
        share = sharing.Share(itsView=self.view, hidden=False)
        item = sharing.SharedItem(sharedFloating)
        item.add()
        item.sharedIn = item.shares = [share]

        nonFloatingOccurrence = master.getNextOccurrence(
            after=datetime(2007, 5, 2, tzinfo=self.view.tzinfo.floating))

        nonFloatingOccurrence.changeThis(
            EventStamp.startTime.name,
            nonFloatingOccurrence.startTime.replace(tzinfo=gayParee))

        titleMod = nonFloatingOccurrence.getNextOccurrence()
        titleMod.itsItem.summary = "yabba dabba doo"

        self.tzprefs.showUI = True

        tzItem = TimeZoneInfo.get(self.view)

        # Make sure that floating is no longer the default
        self.failIfEqual(self.view.tzinfo.default, self.view.tzinfo.floating)
        self.failIfEqual(tzItem.default, self.view.tzinfo.floating)
        self.failUnlessEqual(tzItem.default, self.view.tzinfo.default)

        # Make sure the ordinary and master events acquired the default tz
        self.failUnlessEqual(ordinary.startTime.tzinfo,
                             self.view.tzinfo.default)
        self.failUnlessEqual(master.startTime.tzinfo, self.view.tzinfo.default)

        # Make sure the non-floating occurrence didn't have its tz changed
        self.failUnlessEqual(nonFloatingOccurrence.startTime.tzinfo, gayParee)

        # Check the master's occurrences ...
        for event in map(EventStamp, master.occurrences):
            # Everything but the modification we just made should have
            # the default timezone set for startTime ...
            if event != nonFloatingOccurrence:
                self.failUnlessEqual(event.startTime.tzinfo,
                                     self.view.tzinfo.default)

            # and recurrenceIDs should always have the master's tzinfo
            self.failUnlessEqual(event.recurrenceID.tzinfo,
                                 self.view.tzinfo.default)

        # ... and the shared item's tzinfo should not have changed
        self.failUnlessEqual(sharedFloating.startTime.tzinfo,
                             self.view.tzinfo.floating)

        self.tzprefs.showUI = False
        self.failUnlessEqual(tzItem.default, self.view.tzinfo.floating)
        self.failIfEqual(self.view.tzinfo.floating, self.view.tzinfo.default)
Beispiel #22
0
def _importOneVObject(vobj, filters, coerceTzinfo, promptForTimezone, newItemParent):
    view = newItemParent.itsView

    itemIsNew = False
    newStamps = []

    # by default, we'll create a new item, not change existing items
    itemChangeCallback = None

    # store up all attributes in a dictionary ...
    changesDict = {}

    # ... and define a shorthand for updating it
    def change(attr, value):
        changesDict[attr.name] = value

    # rruleset and userReminderInterval/userReminderTime must
    # be set last....
    changeLast = []

    # values that apply to VEVENT and VTODO ...
    summary = vobj.getChildValue("summary", u"")
    description = vobj.getChildValue("description")
    status = vobj.getChildValue("status", "").lower()
    duration = vobj.getChildValue("duration")
    uid = vobj.getChildValue("uid")
    rruleset = vobj.rruleset
    recurrenceID = vobj.getChildValue("recurrence_id")  # ... uh, sorta
    completed = vobj.getChildValue("completed")

    def convertDatetime(dt):
        # coerce timezones based on coerceTzinfo
        if coerceTzinfo is not None:
            dt = TimeZone.coerceTimeZone(view, dt, coerceTzinfo)

        # ... and make sure we return something with an ICUtzinfo
        return convertToICUtzinfo(view, dt)

    reminderDelta = None
    reminderAbsoluteTime = None

    try:
        reminderValue = vobj.valarm.trigger.value
    except AttributeError:
        pass
    else:
        if type(reminderValue) is datetime.datetime:
            reminderAbsoluteTime = convertDatetime(reminderValue)
        else:
            assert type(reminderValue) is datetime.timedelta
            reminderDelta = reminderValue

    if vobj.name == "VEVENT":

        if DEBUG:
            logger.debug("got VEVENT %s", vobj)

        newStamps.append(EventStamp)
        dtstart = vobj.getChildValue("dtstart")

        if status in ("confirmed", "tentative"):
            pass
        elif status == "cancelled":  # Chandler doesn't have CANCELLED
            status = "fyi"
        else:
            status = "confirmed"

        if EventStamp.transparency.name not in filters:
            change(EventStamp.transparency, status)

        location = vobj.getChildValue("location")
        if location:
            change(EventStamp.location, Calendar.Location.getLocation(view, location))

    elif vobj.name == "VTODO":

        if DEBUG:
            logger.debug("got VEVENT %s", vobj)

        newStamps.append(TaskStamp)

        # VTODO with a DUE ==> EventTask
        due = vobj.getChildValue("due")
        if due is not None:
            newStamps.append(EventStamp)
            dtstart = due

    else:
        assert False, "vobj %s should always be VEVENT or VTODO" % (vobj,)

    # Save changes applicable to both events & tasks ....

    # SUMMARY <-> {EventStamp,TaskStamp}.summary
    if summary is not None:
        change(newStamps[0].summary, summary)

    # DESCRIPTION <-> body
    if description is not None:
        change(Note.body, description)

    # Absolute time reminders
    if reminderAbsoluteTime is not None and Remindable.reminders.name not in filters:
        changeLast.append(lambda item: setattr(item, Remindable.userReminderTime.name, reminderAbsoluteTime))
    # Custom properties/parameters
    ignoredProperties = {}
    ignoredParameters = {}
    for line in vobj.lines():
        name = line.name.lower()
        if name not in attributesUnderstood:
            line.transformFromNative()
            if not line.encoded and line.behavior:
                line.behavior.encode(line)
            ignoredProperties[name] = line.value
        params = u""
        for key, paramvals in line.params.iteritems():
            if key.lower() not in parametersUnderstood:
                vals = map(vobject.base.dquoteEscape, paramvals)
                params += ";" + key + "=" + ",".join(vals)
        if len(params) > 0:
            ignoredParameters[name] = params

    change(Note.icalendarProperties, ignoredProperties)
    change(Note.icalendarParameters, ignoredParameters)

    # See if we have a corresponding item already
    item = utility.findUID(view, uid)

    if item is not None:
        if DEBUG:
            logger.debug("matched UID %s with %s", uid, item)
    else:
        try:
            # See if uid is a valid repository UUID, if so we'll
            # go ahead and use it for the new item's UUID.
            uuid = UUID(uid)
        except ValueError:
            # Not in valid UUID format, so hash the icaluid to
            # generate a 16-byte string we can use for uuid
            uuid = UUID(md5.new(uid).digest())
            logger.info("Converted icalUID '%s' to UUID '%s'", uid, str(uuid))

        # If there is already an item with this UUID, use it,
        # otherwise we'll create one later
        item = view.findUUID(uuid)

        if item is not None:
            item.icalUID = unicode(uuid)

    if EventStamp in newStamps:
        dtend = vobj.getChildValue("dtend")

        isDate = type(dtstart) == date

        # RFC2445 allows VEVENTs without DTSTART, but it's hard to guess
        # what that would mean, so we won't catch an exception if there's no
        # dtstart.
        anyTime = getattr(dtstart, "x_osaf_anytime_param", None) == "TRUE"

        if duration is None:

            def getDifference(left, right):
                leftIsDate = type(left) == date
                rightIsDate = type(right) == date

                if leftIsDate:
                    if rightIsDate:
                        return left - right
                    else:
                        left = TimeZone.forceToDateTime(view, left)

                elif rightIsDate:
                    right = TimeZone.forceToDateTime(view, right)

                return makeNaiveteMatch(view, left, right.tzinfo) - right

            if dtend is not None:
                duration = getDifference(dtend, dtstart)
            elif anyTime or isDate:
                duration = oneDay
            else:
                duration = datetime.timedelta(0)

        if isDate:
            dtstart = TimeZone.forceToDateTime(view, dtstart)
            # convert to Chandler's notion of all day duration
            duration -= oneDay
        elif dtstart.tzinfo is not None and promptForTimezone:
            # got a timezoned event, prompt (non-modally) to turn on
            # timezones
            app = wx.GetApp()
            if app is not None:

                def ShowTimezoneDialogCallback():
                    ShowTurnOnTimezonesDialog(view=app.UIRepositoryView)

                app.PostAsyncEvent(ShowTimezoneDialogCallback)
            promptForTimezone = False

        dtstart = convertDatetime(dtstart)
        tzinfo = dtstart.tzinfo

        if anyTime:
            change(EventStamp.anyTime, True)
            change(EventStamp.allDay, False)
        elif isDate:
            # allDay events should have anyTime True, so if the user
            # unselects allDay, the time isn't set to midnight
            change(EventStamp.anyTime, True)
            change(EventStamp.allDay, True)
        else:
            change(EventStamp.allDay, False)
            change(EventStamp.anyTime, False)

        change(EventStamp.startTime, dtstart)
        change(EventStamp.duration, duration)

        if (reminderDelta is not None) and (Remindable.reminders.name not in filters):
            changeLast.append(lambda item: setattr(item, EventStamp.userReminderInterval.name, reminderDelta))
        if item is not None:
            event = EventStamp(item)

            if recurrenceID:
                if type(recurrenceID) == date:
                    recurrenceID = datetime.datetime.combine(recurrenceID, time(tzinfo=tzinfo))
                else:
                    recurrenceID = convertToICUtzinfo(view, makeNaiveteMatch(view, recurrenceID, tzinfo))

                masterEvent = EventStamp(item)
                event = masterEvent.getRecurrenceID(recurrenceID)
                if event is None and hasattr(masterEvent, "startTime"):
                    # Some calendars, notably Oracle, serialize
                    # recurrence-id as UTC, which wreaks havoc with
                    # noTZ mode. So move recurrenceID to the same tzinfo
                    # as the master's dtstart, bug 6830
                    masterTzinfo = masterEvent.startTime.tzinfo
                    tweakedID = recurrenceID.astimezone(masterTzinfo)
                    event = masterEvent.getRecurrenceID(tweakedID)
                if event is None:
                    # just in case the previous didn't work
                    tweakedID = recurrenceID.astimezone(tzinfo)
                    event = masterEvent.getRecurrenceID(tweakedID)

                if event is None:
                    # our recurrenceID didn't match an item we know
                    # about.  This may be because the item is created
                    # by a later modification, a case we're not dealing
                    # with.  For now, just skip it.
                    logger.info("RECURRENCE-ID '%s' didn't match rule.", recurrenceID)
                    return (None, None, promptForTimezone)

                item = event.itsItem
                recurrenceLine = vobj.contents["recurrence-id"][0]
                range = recurrenceLine.params.get("RANGE", ["THIS"])[0]
                if range == "THISANDPRIOR":
                    # ignore THISANDPRIOR changes for now
                    logger.info("RECURRENCE-ID RANGE of THISANDPRIOR " "not supported")
                    return (None, None, promptForTimezone)
                elif range == "THIS":
                    itemChangeCallback = event.changeThis
                    # check if this is a modification to a master event
                    # if so, avoid changing the master's UUID when
                    # creating a modification
                    if event.getMaster() == event:
                        mod = event._cloneEvent()
                        mod.modificationFor = mod.occurrenceFor = event.itsItem
                        if item.hasLocalAttributeValue(EventStamp.occurrenceFor.name):
                            del event.occurrenceFor
                        event = mod
                        item = event.itsItem

                elif range == "THISANDFUTURE":
                    itemChangeCallback = event.changeThisAndFuture
                else:
                    logger.info("RECURRENCE-ID RANGE not recognized. " "RANGE = %s" % range)
                    return (None, None, promptForTimezone)
            else:
                if event.rruleset is not None:
                    # re-creating a recurring item from scratch, delete
                    # old recurrence information
                    # item might not be the master, though, so
                    # get the master, or eventItem will be a deleted
                    # event
                    event = event.getMaster()
                    item = event.itsItem
                    # delete modifications the master has, to avoid
                    # changing the master to a modification with a
                    # different UUID
                    for mod in itertools.imap(EventStamp, event.modifications):
                        # [Bug 7019]
                        # We need to del these because, in the deferred
                        # delete case, we would have deferred items
                        # living on, in the manner of the undead, in
                        # master.modifications (and occurrences). This
                        # would ultimately cause .getNextOccurrence()
                        # to terminate prematurely.
                        del mod.modificationFor
                        del mod.occurrenceFor
                        mod.itsItem.delete()

                    event.removeRecurrence()

                itemChangeCallback = event.changeThis

            if DEBUG:
                logger.debug("Changing event: %s" % str(event))
            assert itemChangeCallback is not None, "Must set itemChangeCallback for EventStamp imports"

        if rruleset is not None:
            # fix for Bug 6994, exdate and rdate timezones need to be
            # converted to ICUtzinfo instances
            for typ in "_rdate", "_exdate":
                setattr(rruleset, typ, [convertDatetime(d) for d in getattr(rruleset, typ, [])])
            ruleSetItem = RecurrenceRuleSet(None, itsView=view)
            ruleSetItem.setRuleFromDateUtil(rruleset)
            changeLast.append(lambda item: setattr(item, EventStamp.rruleset.name, ruleSetItem))

    if TaskStamp in newStamps:

        if Note._triageStatus.name not in filters:
            # Translate status from iCalendar to TaskStamp/ContentItem
            triageStatus = TriageEnum.now

            if status == "completed":
                triageStatus = TriageEnum.done
            elif status == "needs-action":
                change(Note.needsReply, True)
            elif status in ("", "in-process"):
                change(Note.needsReply, False)
            elif status == "cancelled":
                triageStatus = TriageEnum.later

            # @@@ Jeffrey: This may not be right...
            # Set triageStatus and triageStatusChanged together.
            if completed is not None:
                if type(completed) == date:
                    completed = TimeZone.forceToDateTime(view, completed)
            changeLast.append(lambda item: item.setTriageStatus(triageStatus, when=completed))

    itemIsNew = item is None

    if itemIsNew:

        # create a new item
        change(Note.icalUID, uid)

        kind = Note.getKind(view)
        item = kind.instantiateItem(None, newItemParent, uuid, withInitialValues=True)
        itemChangeCallback = item.__setattr__
    else:
        if itemChangeCallback is None:
            itemChangeCallback = item.__setattr__

        # update an existing item
        if rruleset is None and recurrenceID is None and EventStamp(item).rruleset is not None:
            # no recurrenceId or rruleset, but the existing item
            # may have recurrence, so delete it
            EventStamp(item).removeRecurrence()

    for attr, val in changesDict.iteritems():

        # Only change a datetime if it's really different
        # from what the item already has:
        if type(val) is datetime.datetime:
            oldValue = getattr(item, attr, None)
            if oldValue is not None and oldValue == val and oldValue.tzinfo == val.tzinfo:
                continue

        itemChangeCallback(attr, val)

    # ... make sure the stamps are right
    for stamp in EventStamp, TaskStamp:
        if not stamp in newStamps:
            if has_stamp(item, stamp):
                stamp(item).remove()
        else:
            if not has_stamp(item, stamp):
                stamp(item).add()

    # ... and do the final set of changes
    for cb in changeLast:
        cb(item)

    return item, itemIsNew, promptForTimezone
Beispiel #23
0
def _importOneVObject(vobj, filters, coerceTzinfo, promptForTimezone, 
                      newItemParent):
    view = newItemParent.itsView
    
    itemIsNew = False
    newStamps = []

    # by default, we'll create a new item, not change existing items
    itemChangeCallback = None

    # store up all attributes in a dictionary ...
    changesDict = {}

    # ... and define a shorthand for updating it
    def change(attr, value):
        changesDict[attr.name] = value
        
    # rruleset and userReminderInterval/userReminderTime must
    # be set last....
    changeLast = []

    # values that apply to VEVENT and VTODO ...
    summary     = vobj.getChildValue('summary', u"")
    description = vobj.getChildValue('description')
    status      = vobj.getChildValue('status', "").lower()
    duration    = vobj.getChildValue('duration')
    uid         = vobj.getChildValue('uid')
    rruleset    = vobj.rruleset
    recurrenceID = vobj.getChildValue('recurrence_id') # ... uh, sorta
    completed = vobj.getChildValue('completed')


    def convertDatetime(dt):
        # coerce timezones based on coerceTzinfo
        if coerceTzinfo is not None:
            dt = TimeZone.coerceTimeZone(view, dt, coerceTzinfo)

        # ... and make sure we return something with an ICUtzinfo
        return convertToICUtzinfo(view, dt)

    reminderDelta = None
    reminderAbsoluteTime = None

    try:
        reminderValue = vobj.valarm.trigger.value
    except AttributeError:
        pass
    else:
        if type(reminderValue) is datetime.datetime:
            reminderAbsoluteTime = convertDatetime(reminderValue)
        else:
            assert type(reminderValue) is datetime.timedelta
            reminderDelta = reminderValue
            

    if vobj.name == "VEVENT":

        if DEBUG: logger.debug("got VEVENT %s", vobj)
        
        newStamps.append(EventStamp)
        dtstart = vobj.getChildValue('dtstart')

        if status in ('confirmed', 'tentative'):
            pass
        elif status == 'cancelled': #Chandler doesn't have CANCELLED
            status = 'fyi'
        else:
            status = 'confirmed'

        if EventStamp.transparency.name not in filters:
            change(EventStamp.transparency, status)

        location = vobj.getChildValue('location')
        if location:
            change(EventStamp.location,
                   Calendar.Location.getLocation(view, location))
        
    elif vobj.name == "VTODO":

        if DEBUG: logger.debug("got VEVENT %s", vobj)
        
        newStamps.append(TaskStamp)
        
        # VTODO with a DUE ==> EventTask
        due = vobj.getChildValue('due')
        if due is not None:
            newStamps.append(EventStamp)
            dtstart = due
            
    else:
        assert False, "vobj %s should always be VEVENT or VTODO" % (
                            vobj,)


    # Save changes applicable to both events & tasks ....

    # SUMMARY <-> {EventStamp,TaskStamp}.summary
    if summary is not None:
        change(newStamps[0].summary, summary)

    # DESCRIPTION <-> body  
    if description is not None:
        change(Note.body, description)
        
    # Absolute time reminders
    if (reminderAbsoluteTime is not None and
        Remindable.reminders.name not in filters):
        changeLast.append(lambda item: setattr(item,
                                               Remindable.userReminderTime.name, 
                                               reminderAbsoluteTime))
    # Custom properties/parameters                                           
    ignoredProperties = {}
    ignoredParameters = {}
    for line in vobj.lines():
        name = line.name.lower()
        if name not in attributesUnderstood:
            line.transformFromNative()
            if not line.encoded and line.behavior:
                line.behavior.encode(line)
            ignoredProperties[name] = line.value
        params=u''
        for key, paramvals in line.params.iteritems():
            if key.lower() not in parametersUnderstood:
                vals = map(vobject.base.dquoteEscape, paramvals)
                params += ';' + key + '=' + ','.join(vals)
        if len(params) > 0:
            ignoredParameters[name] = params

    change(Note.icalendarProperties, ignoredProperties)
    change(Note.icalendarParameters, ignoredParameters)


    # See if we have a corresponding item already
    item = utility.findUID(view, uid)

    if item is not None:
        if DEBUG: logger.debug("matched UID %s with %s", uid, item)
    else:
        try:
            # See if uid is a valid repository UUID, if so we'll
            # go ahead and use it for the new item's UUID.
            uuid = UUID(uid)
        except ValueError:
            # Not in valid UUID format, so hash the icaluid to
            # generate a 16-byte string we can use for uuid
            uuid = UUID(md5.new(uid).digest())
            logger.info("Converted icalUID '%s' to UUID '%s'", uid,
                str(uuid))

        # If there is already an item with this UUID, use it,
        # otherwise we'll create one later
        item = view.findUUID(uuid)
            
        if item is not None:
            item.icalUID = unicode(uuid)

            
    if EventStamp in newStamps:
        dtend = vobj.getChildValue('dtend')

        isDate = type(dtstart) == date

        # RFC2445 allows VEVENTs without DTSTART, but it's hard to guess
        # what that would mean, so we won't catch an exception if there's no
        # dtstart.
        anyTime = (getattr(dtstart, 'x_osaf_anytime_param', None)
                      == 'TRUE')
        
        if duration is None:
        
            def getDifference(left, right):
                leftIsDate = (type(left) == date)
                rightIsDate = (type(right) == date)
                
                if leftIsDate:
                    if rightIsDate:
                        return left - right
                    else:
                        left = TimeZone.forceToDateTime(view, left)
                        
                elif rightIsDate:
                    right = TimeZone.forceToDateTime(view, right)

                return makeNaiveteMatch(view, left, right.tzinfo) - right
                
            if dtend is not None:
                duration = getDifference(dtend, dtstart)
            elif anyTime or isDate:
                duration = oneDay
            else:
                duration = datetime.timedelta(0)

        if isDate:
            dtstart = TimeZone.forceToDateTime(view, dtstart)
            # convert to Chandler's notion of all day duration
            duration -= oneDay
        elif dtstart.tzinfo is not None and promptForTimezone:
            # got a timezoned event, prompt (non-modally) to turn on
            # timezones
            app = wx.GetApp()
            if app is not None:
                def ShowTimezoneDialogCallback():
                    ShowTurnOnTimezonesDialog(view=app.UIRepositoryView)
                app.PostAsyncEvent(ShowTimezoneDialogCallback)
            promptForTimezone = False

        dtstart = convertDatetime(dtstart)
        tzinfo = dtstart.tzinfo

        if anyTime:
            change(EventStamp.anyTime, True)
            change(EventStamp.allDay, False)
        elif isDate:
            # allDay events should have anyTime True, so if the user
            # unselects allDay, the time isn't set to midnight
            change(EventStamp.anyTime, True)
            change(EventStamp.allDay, True)
        else:
            change(EventStamp.allDay, False)
            change(EventStamp.anyTime, False)

        change(EventStamp.startTime, dtstart)
        change(EventStamp.duration, duration)

        if ((reminderDelta is not None) and
            (Remindable.reminders.name not in filters)):
                changeLast.append(
                    lambda item:setattr(item,
                                        EventStamp.userReminderInterval.name, 
                                        reminderDelta))
        if item is not None:
            event = EventStamp(item)

            if recurrenceID:
                if type(recurrenceID) == date:
                    recurrenceID = datetime.datetime.combine(
                                                recurrenceID,
                                                time(tzinfo=tzinfo))
                else:
                    recurrenceID = convertToICUtzinfo(view, 
                                       makeNaiveteMatch(view, recurrenceID,
                                       tzinfo))
                
                masterEvent = EventStamp(item)    
                event = masterEvent.getRecurrenceID(recurrenceID)
                if event is None and hasattr(masterEvent, 'startTime'):
                    # Some calendars, notably Oracle, serialize
                    # recurrence-id as UTC, which wreaks havoc with 
                    # noTZ mode. So move recurrenceID to the same tzinfo
                    # as the master's dtstart, bug 6830
                    masterTzinfo = masterEvent.startTime.tzinfo
                    tweakedID = recurrenceID.astimezone(masterTzinfo)
                    event = masterEvent.getRecurrenceID(tweakedID)
                if event is None:
                    # just in case the previous didn't work
                    tweakedID = recurrenceID.astimezone(tzinfo)
                    event = masterEvent.getRecurrenceID(tweakedID)
                    
                if event is None:
                    # our recurrenceID didn't match an item we know
                    # about.  This may be because the item is created
                    # by a later modification, a case we're not dealing
                    # with.  For now, just skip it.
                    logger.info("RECURRENCE-ID '%s' didn't match rule.",
                                recurrenceID)
                    return (None, None, promptForTimezone)

                item = event.itsItem
                recurrenceLine = vobj.contents['recurrence-id'][0]
                range = recurrenceLine.params.get('RANGE', ['THIS'])[0]
                if range == 'THISANDPRIOR':
                    # ignore THISANDPRIOR changes for now
                    logger.info("RECURRENCE-ID RANGE of THISANDPRIOR " \
                                "not supported")
                    return (None, None, promptForTimezone)
                elif range == 'THIS':
                    itemChangeCallback = event.changeThis
                    # check if this is a modification to a master event
                    # if so, avoid changing the master's UUID when
                    # creating a modification
                    if event.getMaster() == event:
                        mod = event._cloneEvent()
                        mod.modificationFor = mod.occurrenceFor = event.itsItem
                        if item.hasLocalAttributeValue(
                                        EventStamp.occurrenceFor.name):
                            del event.occurrenceFor
                        event = mod
                        item = event.itsItem
                    
                elif range == 'THISANDFUTURE':
                    itemChangeCallback = event.changeThisAndFuture
                else:
                    logger.info("RECURRENCE-ID RANGE not recognized. " \
                                "RANGE = %s" % range)
                    return (None, None, promptForTimezone)
            else:
                if event.rruleset is not None:
                    # re-creating a recurring item from scratch, delete 
                    # old recurrence information
                    # item might not be the master, though, so
                    # get the master, or eventItem will be a deleted
                    # event
                    event = event.getMaster()
                    item = event.itsItem
                    # delete modifications the master has, to avoid
                    # changing the master to a modification with a
                    # different UUID
                    for mod in itertools.imap(EventStamp, event.modifications):
                        # [Bug 7019]
                        # We need to del these because, in the deferred
                        # delete case, we would have deferred items
                        # living on, in the manner of the undead, in
                        # master.modifications (and occurrences). This
                        # would ultimately cause .getNextOccurrence()
                        # to terminate prematurely.
                        del mod.modificationFor
                        del mod.occurrenceFor
                        mod.itsItem.delete()

                    event.removeRecurrence()
                    
                itemChangeCallback = event.changeThis

            if DEBUG: logger.debug("Changing event: %s" % str(event))
            assert itemChangeCallback is not None, \
                   "Must set itemChangeCallback for EventStamp imports"
                

        if rruleset is not None:
            # fix for Bug 6994, exdate and rdate timezones need to be
            # converted to ICUtzinfo instances
            for typ in '_rdate', '_exdate':
                setattr(rruleset, typ, [convertDatetime(d) for d in
                                        getattr(rruleset, typ, [])  ])
            ruleSetItem = RecurrenceRuleSet(None, itsView=view)
            ruleSetItem.setRuleFromDateUtil(rruleset)
            changeLast.append(lambda item: setattr(item,
                                                   EventStamp.rruleset.name,
                                                   ruleSetItem))
        
    if TaskStamp in newStamps:
        
        if Note._triageStatus.name not in filters:
            # Translate status from iCalendar to TaskStamp/ContentItem
            triageStatus=TriageEnum.now
            
            if status == "completed":
                triageStatus = TriageEnum.done
            elif status == "needs-action":
                change(Note.needsReply, True)
            elif status in ("", "in-process"):
                change(Note.needsReply, False)
            elif status == "cancelled":
                triageStatus = TriageEnum.later

            # @@@ Jeffrey: This may not be right...
            # Set triageStatus and triageStatusChanged together.
            if completed is not None:
                if type(completed) == date:
                    completed = TimeZone.forceToDateTime(view, completed)
            changeLast.append(lambda item: item.setTriageStatus(triageStatus, 
                                                                when=completed))
            

    itemIsNew = (item is None)
            
    if itemIsNew:

        # create a new item
        change(Note.icalUID, uid)

        kind = Note.getKind(view)
        item = kind.instantiateItem(None, newItemParent, uuid,
                                    withInitialValues=True)
        itemChangeCallback = item.__setattr__
    else:
        if itemChangeCallback is None:
            itemChangeCallback = item.__setattr__
        
        # update an existing item
        if (rruleset is None and recurrenceID is None
           and EventStamp(item).rruleset is not None):
            # no recurrenceId or rruleset, but the existing item
            # may have recurrence, so delete it
            EventStamp(item).removeRecurrence()

    for attr, val in changesDict.iteritems():
    
        # Only change a datetime if it's really different
        # from what the item already has:
        if type(val) is datetime.datetime:
             oldValue = getattr(item, attr, None)
             if (oldValue is not None and 
                 oldValue == val and
                 oldValue.tzinfo == val.tzinfo):
                continue
    
        itemChangeCallback(attr, val)

    # ... make sure the stamps are right
    for stamp in EventStamp, TaskStamp:
        if not stamp in newStamps:
            if has_stamp(item, stamp):
                stamp(item).remove()
        else:
            if not has_stamp(item, stamp):
                stamp(item).add()

    # ... and do the final set of changes
    for cb in changeLast:
        cb(item)
        
            
    return item, itemIsNew, promptForTimezone