def ItemsToVobject(self): """Tests itemsToVObject, which converts Chandler items to vobject.""" event = Calendar.CalendarEvent(view = self.repo.view) event.displayName = "test" event.startTime = datetime.datetime(2010, 1, 1, 10) event.endTime = datetime.datetime(2010, 1, 1, 11) cal = ICalendar.itemsToVObject(self.repo.view, [event]) self.assert_(cal.vevent[0].summary[0].value == "test", "summary not set properly, summary is %s" % cal.vevent[0].summary[0].value) start = event.startTime.replace(tzinfo=ICalendar.localtime) self.assert_(cal.vevent[0].dtstart[0].value == start, "dtstart not set properly, dtstart is %s" % cal.vevent[0].summary[0].value) event = Calendar.CalendarEvent(view = self.repo.view) event.displayName = "test2" event.startTime = datetime.datetime(2010, 1, 1) event.allDay = True cal = ICalendar.itemsToVObject(self.repo.view, [event]) self.assert_(cal.vevent[0].dtstart[0].value == datetime.date(2010,1,1), "dtstart for allDay event not set properly, dtstart is %s" % cal.vevent[0].summary[0].value)
def itemsToFreeBusy(view, start, end, calname = None): """ Create FREEBUSY components corresponding to all events between start and end. """ all = schema.ns("osaf.pim", view).allCollection normal = Calendar.eventsInRange(view, start, end, all) recurring = Calendar.recurringEventsInRange(view, start, end, all) events = Calendar._sortEvents(itertools.chain(normal, recurring), attrName='effectiveStartTime') def toUTC(dt): if dt < start: dt = start elif dt > end: dt = end return translateToTimezone(dt, view.tzinfo.UTC) cal = vobject.iCalendar() if calname is not None: cal.add('x-wr-calname').value = calname vfree = cal.add('vfreebusy') vfree.add('dtstart').value = toUTC(start) vfree.add('dtend').value = toUTC(end) def addFB(event): free = vfree.add('freebusy') free.fbtype_param = transparencyMap[event.transparency] return free free = None for event in events: # ignore anytime events, events with no duration, and fyi events if (event.transparency == 'fyi' or ((event.anyTime or event.duration == datetime.timedelta(0)) and not event.allDay)): continue if free is None or free.fbtype_param != \ transparencyMap[event.transparency]: free = addFB(event) free.value = [[toUTC(event.effectiveStartTime), event.effectiveEndTime]] else: # compress freebusy blocks if possible if event.effectiveStartTime <= free.value[-1][1]: if event.effectiveEndTime > free.value[-1][1]: free.value[-1][1] = event.effectiveEndTime else: free.value.append([toUTC(event.effectiveStartTime), event.effectiveEndTime]) # once upon a time serialize would convert (date, date) to (date, period) # but no longer. So there's no need to serialize # vfree.serialize() return cal
def itemsToFreeBusy(view, start, end, calname=None): """ Create FREEBUSY components corresponding to all events between start and end. """ all = schema.ns("osaf.pim", view).allCollection normal = Calendar.eventsInRange(view, start, end, all) recurring = Calendar.recurringEventsInRange(view, start, end, all) events = Calendar._sortEvents(itertools.chain(normal, recurring), attrName="effectiveStartTime") def toUTC(dt): if dt < start: dt = start elif dt > end: dt = end return translateToTimezone(dt, view.tzinfo.UTC) cal = vobject.iCalendar() if calname is not None: cal.add("x-wr-calname").value = calname vfree = cal.add("vfreebusy") vfree.add("dtstart").value = toUTC(start) vfree.add("dtend").value = toUTC(end) def addFB(event): free = vfree.add("freebusy") free.fbtype_param = transparencyMap[event.transparency] return free free = None for event in events: # ignore anytime events, events with no duration, and fyi events if event.transparency == "fyi" or ( (event.anyTime or event.duration == datetime.timedelta(0)) and not event.allDay ): continue if free is None or free.fbtype_param != transparencyMap[event.transparency]: free = addFB(event) free.value = [[toUTC(event.effectiveStartTime), event.effectiveEndTime]] else: # compress freebusy blocks if possible if event.effectiveStartTime <= free.value[-1][1]: if event.effectiveEndTime > free.value[-1][1]: free.value[-1][1] = event.effectiveEndTime else: free.value.append([toUTC(event.effectiveStartTime), event.effectiveEndTime]) # once upon a time serialize would convert (date, date) to (date, period) # but no longer. So there's no need to serialize # vfree.serialize() return cal
def testNone(self): showTZUI = False adjustStart, adjustEnd = Calendar.adjustSearchTimes(self.start, None, showTZUI) self.failUnless(adjustEnd is None) self.failIf(adjustStart is None) self.failIfEqual(adjustStart, self.start, "If showTZUI is False, should adjust start") adjustStart, adjustEnd = Calendar.adjustSearchTimes(None, self.end, showTZUI) self.failUnless(adjustStart is None) self.failIf(adjustEnd is None) self.failIfEqual(adjustEnd, self.end, "If showTZUI is False, should adjust end")
def GenerateMailMessage(view, tzinfo=None): global M_FROM message = Mail.MailMessage(itsView=view) body = M_TEXT outbound = random.randint(0, 1) type = random.randint(1, 8) numTo = random.randint(1, 3) if M_FROM is None: M_FROM = GenerateCalendarParticipant(view) message.fromAddress = M_FROM for num in range(numTo): message.toAddress.append(GenerateCalendarParticipant(view)) message.subject = random.choice(TITLES) if TEST_I18N: message.subject = uw(message.subject) message.dateSent = datetime.now(tzinfo) if outbound: message.outgoingMessage() message.itsItem.changeEditState(Modification.sent, who=M_FROM) else: message.incomingMessage() if type == EVENT: Calendar.EventStamp(message).add() body += M_EVENT if type == TASK: pim.TaskStamp(message).add() body += M_TASK if type == BOTH: Calendar.EventStamp(message).add() pim.TaskStamp(message).add() body += M_BOTH if TEST_I18N: body = uw(body) message.body = body message.itsItem.setTriageStatus(randomEnum(pim.TriageEnum)) return message.itsItem
def testICSSerializer(self): """Tests serialization of items to icalendar streams.""" event = Calendar.CalendarEvent(itsView=self.view) event.anyTime = False event.summary = uw("test") event.startTime = datetime.datetime(2010, 1, 1, 10, tzinfo=self.view.tzinfo.default) event.endTime = datetime.datetime(2010, 1, 1, 11, tzinfo=self.view.tzinfo.default) cal = getVObjectData(self.view, [event.itsItem]) self.failUnlessEqual( cal.vevent.summary.value, uw("test"), u"summary not set properly, summary is %s" % cal.vevent.summary.value) start = event.startTime self.assert_( cal.vevent.dtstart.value == start, "dtstart not set properly, dtstart is %s" % cal.vevent.summary.value) event = Calendar.CalendarEvent(itsView=self.view) event.summary = uw("test2") event.startTime = datetime.datetime(2010, 1, 1, tzinfo=self.view.tzinfo.floating) event.allDay = True event.duration = datetime.timedelta(1) self.assertEqual(event.effectiveEndTime - event.effectiveStartTime, datetime.timedelta(2)) cal = getVObjectData(self.view, [event.itsItem]) self.assert_( cal.vevent.dtstart.value == datetime.date(2010, 1, 1), u"dtstart for allDay event not set properly, dtstart is %s" % cal.vevent.summary.value) # test bug 9137, multi-day all-day events with # duration modulo 1 day == 0 are serialized as 1 day instead of 2 self.assertEqual(cal.vevent.duration.value, datetime.timedelta(2))
def testMultidayEdgeCase(self): tzinfo = self.sandbox.itsView.tzinfo event = Calendar.CalendarEvent( itsParent=self.sandbox, allDay=True, anyTime=False, startTime=datetime(2007, 7, 20, tzinfo=tzinfo.floating), duration=timedelta(days=0), ) self.failUnlessEqual(event.effectiveStartTime, datetime(2007, 7, 20, tzinfo=tzinfo.floating)) self.failUnlessEqual(event.effectiveEndTime, datetime(2007, 7, 21, tzinfo=tzinfo.floating)) event.duration = timedelta(days=1) # Really, the following should be 2007/07/21. However, for a while # Chandler has treated an all-day event with duration of exactly # one day as lasting two days. Jeffrey pointed out that there is # import/export code we would need to change in addition to the # domain model here. [grant 2007/10/17] self.failUnlessEqual(event.effectiveEndTime, datetime(2007, 7, 22, tzinfo=tzinfo.floating)) event.duration = timedelta(days=2, hours=3) self.failUnlessEqual(event.effectiveEndTime, datetime(2007, 7, 23, tzinfo=tzinfo.floating))
def testAllDay(self): event = Calendar.CalendarEvent(itsParent=self.sandbox, anyTime=False, allDay=True) self.failUnlessEqual(event.effectiveStartTime.timetz(), self.floatingMidnight) self.failUnlessEqual(event.effectiveStartTime.date(), event.startTime.date()) self.failUnlessEqual(event.effectiveEndTime, event.effectiveStartTime + timedelta(days=1)) event.anyTime = True self.failUnlessEqual(event.effectiveStartTime.timetz(), self.floatingMidnight) self.failUnlessEqual(event.effectiveStartTime.date(), event.startTime.date()) self.failUnlessEqual(event.effectiveEndTime, event.effectiveStartTime + timedelta(days=1)) event.anyTime = event.allDay = False self.failUnlessEqual(event.startTime, event.effectiveStartTime) self.failUnlessEqual(event.effectiveStartTime.date(), event.startTime.date()) self.failUnlessEqual(event.effectiveEndTime, event.effectiveStartTime + event.duration)
def _createEvent(self, startTime): event = Calendar.CalendarEvent(None, itsView=self.view) event.startTime = startTime event.endTime = event.startTime + timedelta(hours=1) event.anyTime = False event.summary = uw("Sample event") return event
def testEventIndex(self): """Make sure eventsInRange works.""" daysEvents = list(Calendar.eventsInRange(self.view, self.midnight, self.midnight + timedelta(1))) self.assertEqual(daysEvents[0], self.pacificEvent) self.assertEqual(daysEvents[1], self.floatingEvent) self.assertEqual(daysEvents[2], self.hawaiiEvent)
def testDefaultSettings(self): defaultTz = TimeZoneInfo.get(self.sandbox.itsView).default ROUND = 30 # A new event, by default, has its startTime set to the current time # rounded to a 30-minute boundary start = datetime.now(defaultTz) event = Calendar.CalendarEvent(itsParent=self.sandbox, anyTime=False, allDay=False) end = datetime.now(defaultTz) # Check that it falls within 30 minutes of now self.failUnless(start - timedelta(minutes=ROUND) <= event.startTime and event.startTime <= end + timedelta(minutes=ROUND)) # Check that the correct rounding occurred self.failUnlessEqual(event.startTime.second, 0) self.failUnlessEqual(event.startTime.microsecond, 0) self.failUnlessEqual(event.startTime.minute % ROUND, 0) # Make sure it's in the right timezone self.failUnlessEqual(event.startTime.tzinfo, defaultTz) # Lastly, check the dates if possible if start.date() == end.date(): self.failUnlessEqual(start.date(), event.startTime.date()) self.failUnlessEqual(event.effectiveStartTime, event.startTime) self.failUnlessEqual(event.effectiveEndTime, event.endTime)
def testNone(self): showTZUI = False adjustStart, adjustEnd = Calendar.adjustSearchTimes( self.start, None, showTZUI) self.failUnless(adjustEnd is None) self.failIf(adjustStart is None) self.failIfEqual(adjustStart, self.start, "If showTZUI is False, should adjust start") adjustStart, adjustEnd = Calendar.adjustSearchTimes( None, self.end, showTZUI) self.failUnless(adjustStart is None) self.failIf(adjustEnd is None) self.failIfEqual(adjustEnd, self.end, "If showTZUI is False, should adjust end")
def setUp(self): if not hasattr(RecurrenceConflictTestCase, 'view'): super(RecurrenceConflictTestCase, self).setUp() RecurrenceConflictTestCase.view = self.view del self.view view = RecurrenceConflictTestCase.view self.sandbox = Item("sandbox", view, None) self.pacific = self.view.tzinfo.getInstance("America/Los_Angeles") self.floating = self.view.tzinfo.floating self.utc = self.view.tzinfo.UTC self.start = datetime.datetime(2007, 4, 10, 9, tzinfo=self.floating) self.master = Calendar.CalendarEvent(None, itsParent=self.sandbox) self.uuid = self.master.itsItem.itsUUID.str16() self.master.startTime = self.start self.master.anyTime = self.master.allDay = False # create a baseline dictionary with keywords for creating an EventRecord names = (i.name for i in EventRecord.__fields__ if i.name != 'uuid') # default values to NoChange self.kwds = dict.fromkeys(names, sharing.NoChange)
def testAtTime(self): event = Calendar.CalendarEvent(itsParent=self.sandbox, anyTime=False, allDay=False, duration=timedelta(0)) self.failUnlessEqual(event.effectiveStartTime, event.startTime) self.failUnlessEqual(event.endTime, event.startTime) self.failUnlessEqual(event.effectiveEndTime, event.startTime)
def testExportRecurrence(self): eastern = self.view.tzinfo.getInstance("America/New_York") start = datetime.datetime(2005, 2, 1, tzinfo=eastern) vevent = vobject.icalendar.RecurringComponent(name='VEVENT') vevent.behavior = vobject.icalendar.VEvent vevent.add('dtstart').value = start ruleSetItem = self._makeRecurrenceRuleSet() vevent.rruleset = ruleSetItem.createDateUtilFromRule(start) self.assertEqual(vevent.rrule.value, 'FREQ=DAILY') event = Calendar.CalendarEvent(itsView=self.view) event.anyTime = False event.summary = uw("blah") event.startTime = start event.endTime = datetime.datetime(2005, 2, 1, 1, tzinfo=eastern) event.rruleset = self._makeRecurrenceRuleSet( datetime.datetime(2005, 3, 1, tzinfo=eastern), freq='weekly', byweekday=[ WeekdayAndPositionStruct(i, 0) for i in "tuesday", "thursday" ]) vcalendar = getVObjectData(self.view, [event.itsItem]) self.assertEqual(vcalendar.vevent.dtstart.serialize(), 'DTSTART;TZID=America/New_York:20050201T000000\r\n') vcalendar.vevent = vcalendar.vevent.transformFromNative() self.assertEqual( vcalendar.vevent.rrule.serialize(), 'RRULE:FREQ=WEEKLY;BYDAY=TU,TH;UNTIL=20050302T045900Z\r\n') # move the second occurrence one day later nextEvent = event.getFirstOccurrence().getNextOccurrence() nextEvent.changeThis( pim.EventStamp.startTime.name, datetime.datetime(2005, 2, 9, tzinfo=self.view.tzinfo.floating)) nextEvent.getNextOccurrence().deleteThis() vcalendar = getVObjectData(self.view, [event.itsItem, nextEvent]) for ev in vcalendar.vevent_list: if hasattr(ev, 'recurrence_id'): modified = ev else: master = ev self.assertEqual(modified.dtstart.serialize(), 'DTSTART:20050209T000000\r\n') self.assertEqual( modified.recurrence_id.serialize(), 'RECURRENCE-ID;TZID=America/New_York:20050203T000000\r\n') self.assertEqual(master.exdate.serialize(), 'EXDATE;TZID=America/New_York:20050210T000000\r\n') vcalendar.behavior.generateImplicitParameters(vcalendar) self.assertEqual(vcalendar.vtimezone.tzid.value, "America/New_York")
def testTimeFields(self): """ Test time related fields and methods """ self.loadParcel("parcel:osaf.pim.calendar") # Test getDuration view = self.rep.view firstItem = Calendar.CalendarEvent(view=view) firstItem.anyTime = False firstItem.startTime = datetime(2003, 2, 1, 10) firstItem.endTime = datetime(2003, 2, 1, 11, 30) self.assertEqual(firstItem.duration, timedelta(hours=1.5)) # Test setDuration secondItem = Calendar.CalendarEvent(view=view) secondItem.anyTime = False secondItem.startTime = datetime(2003, 3, 5, 9) secondItem.duration = timedelta(hours=1.5) self.assertEqual(secondItem.endTime, datetime(2003, 3, 5, 10, 30)) # Test changeStartTime firstItem.ChangeStart(datetime(2003, 3, 4, 12, 45)) self.assertEqual(firstItem.duration, timedelta(hours=1.5)) self.assertEqual(firstItem.startTime, datetime(2003, 3, 4, 12, 45)) # Test reminderTime firstItem.SetReminderDelta(timedelta(minutes=-30)) self.assertEqual(firstItem.reminderTime, datetime(2003, 3, 4, 12, 15)) firstItem.SetReminderDelta(None) self.failIf(firstItem.hasLocalAttributeValue('reminderTime')) # Test allDay firstItem.allDay = True self.assertEqual(firstItem.allDay, True) firstItem.allDay = False self.assertEqual(firstItem.allDay, False) # Test anyTime firstItem.anyTime = True self.assertEqual(firstItem.anyTime, True) firstItem.anyTime = False self.assertEqual(firstItem.anyTime, False)
def testCreate(self): eventCreateDict = { 'itsView': self.view, 'summary': u'event creation test', Note.icalUID.name: u'abcdef', 'startTime': datetime.datetime(1996, 11, 11) } event = Calendar.CalendarEvent(**eventCreateDict) self.failUnlessEqual(u'abcdef', event.itsItem.icalUID)
def testReorderFloating(self): """Changes to floating time should cause events to be reindexed.""" self.tzInfoItem.default = self.eastern daysEvents = list(Calendar.eventsInRange(self.view, self.midnight, self.midnight + timedelta(1))) #print [i.startTime for i in daysEvents] self.assertEqual(daysEvents[0], self.easternEvent) self.assertEqual(daysEvents[1], self.floatingEvent) self.assertEqual(daysEvents[2], self.pacificEvent) self.assertEqual(daysEvents[3], self.hawaiiEvent)
def testWithoutTimeZones(self): showTZUI = False adjustStart, adjustEnd = Calendar.adjustSearchTimes(self.start, self.end, showTZUI) self.failIfEqual(adjustStart, self.start, "If showTZUI is False, should adjust start") self.failIfEqual(adjustEnd, self.end, "If showTZUI is False, should adjust end") expected = [adjustStart, self.dt2, self.start, self.dt1, self.end, adjustEnd] sortedTimes = sorted(expected, key=lambda dt: dt.replace(tzinfo=None)) self.failUnlessEqual( sortedTimes, expected, "Misordered datetimes: got %s; expected %s" % (expected, sortedTimes) )
def testWithTimeZones(self): showTZUI = True adjustStart, adjustEnd = Calendar.adjustSearchTimes(self.start, self.end, showTZUI) self.failUnlessEqual(adjustStart, self.start, "If showTZUI is True, shouldn't adjust start") self.failUnlessEqual(adjustEnd, self.end, "If showTZUI is True, shouldn't adjust end") expected = [self.dt1, adjustStart, self.dt2, adjustEnd] sortedTimes = sorted(expected) self.failUnlessEqual( sortedTimes, expected, "Misordered datetimes: got %s; expected %s" % (expected, sortedTimes) )
def __init__(self, view, type, logger): if not type in ["Event", "Note", "Task", "MailMessage", "Collection"]: return else: self.isNote = self.isEvent = self.isTask = self.isMessage = self.allDay = False self.logger = logger now = datetime.now() if type == "Event": # New Calendar Event # set up the expected data dictionary with the default values self.expected_field_dict = {"displayName" : "New Event", "startTime" : now, "endTime" : now, "duration" : timedelta(minutes=60)} # create a default Calendar Event item = Calendar.CalendarEvent(view=view) item.startTime = self.expected_field_dict["startTime"] # because startTime is needed befor duration self.isEvent = True elif type == "Note": # New Note # set up the expected data dictionary with the default values self.expected_field_dict = {"displayName" : "New Note", "createdOn" : now} # create a default Note item = pim.Note(view=view) self.isNote = True elif type == "Task": # New Task # set up the expected data dictionary with the default values self.expected_field_dict = {"displayName" : "New Task", "createdOn" : now} # create a default Task item = pim.Task(view=view) self.isTask = True elif type == "MailMessage": # New Mail Message # set up the expected data dictionary with the default values email = Mail.EmailAddress(view=view) email.emailAddress = 'me' self.expected_field_dict = {"subject" : "untitled", "dateSent" : now, "fromAddress" : email} # create a default Message item = Mail.MailMessage(view=view) self.isMessage = True elif type == "Collection": # New Collection # set up the expected data dictionary with the default values self.expected_field_dict = {"displayName" : "Untitled"} # create a default Collection item = pim.ItemCollection(view=view) # fields affectation for field in self.expected_field_dict.keys(): setattr(item, field, self.expected_field_dict[field]) self.item = item if type =="Collection": Sgf.SidebarAdd(self.item) Sgf.SidebarSelect(self.item) else: Sgf.SummaryViewSelect(self.item)
def testDeleteItem(self): """ Test calendar event deletion """ view = self.sandbox.itsView event = Calendar.CalendarEvent(itsParent=self.sandbox) item = event.itsItem path = item.itsPath item.delete() del item self.sandbox.itsView.commit() itemShouldBeGone = view.find(path) self.assertEqual(itemShouldBeGone, None)
def testUpdate(self): uid = u'on-a-limo-to-milano-solo-gigolos-dont-nod' task = pim.Task(itsView=self.view) task.itsItem.icalUID = uid event = Calendar.CalendarEvent(itsView=self.view, startTime=datetime.datetime(2000, 1, 1)) event.itsItem.icalUID = uid del task.itsItem.icalUID # Necessary? self.failUnless(event.itsItem is sharing.findUID(self.view, uid))
def testDayBoundary(self): tzinfo = self.sandbox.itsView.tzinfo event = Calendar.CalendarEvent( itsParent=self.sandbox, allDay=True, anyTime=False, startTime=datetime(2007, 10, 16, 23, 30, tzinfo=tzinfo.default), duration=timedelta(hours=1), ) self.failUnlessEqual(event.effectiveStartTime, datetime(2007, 10, 16, tzinfo=tzinfo.floating)) self.failUnlessEqual(event.effectiveEndTime, datetime(2007, 10, 17, tzinfo=tzinfo.floating))
def testProxy(self): self.failIf(self.event.isProxy()) proxy = Calendar.getProxy(self.event) self.assert_(proxy.isProxy()) self.assertEqual(proxy, self.event) self.assertEqual(proxy.currentlyModifying, None) proxy.rruleset = self._createRuleSetItem('weekly') self.assert_(self.event in proxy.rruleset.events) self.assertEqual(proxy.getNextOccurrence().occurrenceFor, self.event) self.assertEqual(len(list(proxy._generateRule())), self.weekly['count']) proxy.startTime = self.start + timedelta(days=1)
def GenerateCalendarEvent(view, days=30, tzinfo=None): if tzinfo is None: tzinfo = view.tzinfo.floating event = Calendar.CalendarEvent(itsView=view) event.summary = random.choice(HEADLINES) if TEST_I18N: event.summary = uw(event.summary) # Choose random days, hours startDelta = timedelta(days=random.randint(0, days), 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) event.startTime = closeToNow + startDelta # Events are anyTime by default. Give a 5% chance of allDay instead, # or 90% of a normal event. r = random.randint(0, 100) if r < 95: # 5% chance that we'll leave anyTime on event.anyTime = False if r < 5: # 5% chance of allDay event.allDay = True # Choose random minutes event.duration = timedelta(minutes=random.choice(DURATIONS)) # Maybe a nice reminder? reminderInterval = random.choice(REMINDERS) if reminderInterval is not None: event.userReminderInterval = timedelta(minutes=-reminderInterval) # Add a location to 2/3 of the events if random.randrange(3) > 0: if TEST_I18N: event.location = Calendar.Location.getLocation( view, uw(random.choice(LOCATIONS))) else: event.location = Calendar.Location.getLocation( view, random.choice(LOCATIONS)) event.itsItem.importance = random.choice(pim.ImportanceEnum.values) event.itsItem.setTriageStatus(randomEnum(pim.TriageEnum)) return event.itsItem
def testDeleteItem(self): """ Test calendar event deletion """ self.loadParcel("parcel:osaf.pim.calendar") view = self.rep.view item = Calendar.CalendarEvent(view=view) path = item.itsPath item.delete() del item itemShouldBeGone = view.find(path) self.assertEqual(itemShouldBeGone, None) view.commit()
def testTimeFields(self): """ Test time related fields and methods """ # Test getting duration, setting endTime defaultTz = self.sandbox.itsView.tzinfo.default firstEvent = Calendar.CalendarEvent(itsParent=self.sandbox) firstEvent.anyTime = False firstEvent.startTime = datetime(2003, 2, 1, 10, tzinfo=defaultTz) firstEvent.endTime = datetime(2003, 2, 1, 11, 30, tzinfo=defaultTz) self.assertEqual(firstEvent.duration, timedelta(hours=1.5)) # Test setting duration and getting endTime secondEvent = Calendar.CalendarEvent(itsParent=self.sandbox) secondEvent.anyTime = False secondEvent.startTime = datetime(2003, 3, 5, 9, tzinfo=defaultTz) secondEvent.duration = timedelta(hours=1.5) self.assertEqual(secondEvent.endTime, datetime(2003, 3, 5, 10, 30, tzinfo=defaultTz)) # Test changing startTime (shouldn't change duration) firstEvent.startTime = datetime(2003, 3, 4, 12, 45, tzinfo=defaultTz) self.assertEqual(firstEvent.duration, timedelta(hours=1.5)) self.assertEqual(firstEvent.startTime, datetime(2003, 3, 4, 12, 45, tzinfo=defaultTz)) # Test allDay firstEvent.allDay = True self.assertEqual(firstEvent.allDay, True) firstEvent.allDay = False self.assertEqual(firstEvent.allDay, False) # Test anyTime firstEvent.anyTime = True self.assertEqual(firstEvent.anyTime, True) firstEvent.anyTime = False self.assertEqual(firstEvent.anyTime, False)
def testTimed(self): event = Calendar.CalendarEvent( itsParent=self.sandbox, anyTime=False, allDay=False, startTime=datetime(2006, 11, 4, 13, 25, tzinfo=self.sandbox.itsView.tzinfo.default), duration=timedelta(hours=1)) self.failUnlessEqual(event.effectiveStartTime, event.startTime) self.failUnlessEqual(event.endTime - timedelta(hours=1), event.startTime) self.failUnlessEqual(event.effectiveEndTime, event.endTime)
def testWithTimeZones(self): showTZUI = True adjustStart, adjustEnd = Calendar.adjustSearchTimes( self.start, self.end, showTZUI) self.failUnlessEqual(adjustStart, self.start, "If showTZUI is True, shouldn't adjust start") self.failUnlessEqual(adjustEnd, self.end, "If showTZUI is True, shouldn't adjust end") expected = [self.dt1, adjustStart, self.dt2, adjustEnd] sortedTimes = sorted(expected) self.failUnlessEqual( sortedTimes, expected, "Misordered datetimes: got %s; expected %s" % (expected, sortedTimes))
def testFindEventUID(self): uid = u'123' self.failUnless(sharing.findUID(self.view, uid) is None) event = Calendar.CalendarEvent(itsView=self.view, displayName=u"event test", startTime=datetime.datetime( 2010, 1, 1, 10), duration=datetime.timedelta(hours=2)) self.failUnless(sharing.findUID(self.view, uid) is None) item = event.itsItem setattr(item, Note.icalUID.name, uid) self.failUnless(sharing.findUID(self.view, uid) is item)
def testWithoutTimeZones(self): showTZUI = False adjustStart, adjustEnd = Calendar.adjustSearchTimes( self.start, self.end, showTZUI) self.failIfEqual(adjustStart, self.start, "If showTZUI is False, should adjust start") self.failIfEqual(adjustEnd, self.end, "If showTZUI is False, should adjust end") expected = [ adjustStart, self.dt2, self.start, self.dt1, self.end, adjustEnd ] sortedTimes = sorted(expected, key=lambda dt: dt.replace(tzinfo=None)) self.failUnlessEqual( sortedTimes, expected, "Misordered datetimes: got %s; expected %s" % (expected, sortedTimes))
def setUp(self): super(RecurringEventTest, self).setUp() self.start = datetime(2005, 7, 4, 13) #1PM, July 4, 2005 self.weekly = { 'end': datetime(2005, 11, 14, 13), 'start': self.start, 'count': 20 } self.monthly = { 'end': datetime(2005, 11, 4, 13), 'start': self.start, 'count': 5 } self.event = Calendar.CalendarEvent(None, view=self.rep.view) self.event.startTime = self.start self.event.endTime = self.event.startTime + timedelta(hours=1) self.event.anyTime = False self.event.displayName = "Sample event"
def setFreeBusy(self): if self._recalcCount == 0: zerotime = time(tzinfo=self.blockItem.itsView.tzinfo.default) start = self.GetStartDate() start = datetime.combine(start, zerotime) # ugh, why can't timedelta just support months? end = minical.MonthDelta(start, 3) end = datetime.combine(end, zerotime) if self.HasPendingEventChanges(): addedItems, removedItems, changedItems = \ self.GetPendingChanges(False) if len(removedItems) + len(changedItems) > 0: self._recalcCount += 1 self._eventsToAdd.clear() else: for item in addedItems: if not isDead(item): if (not has_stamp(item, EventStamp)): continue event = Calendar.EventStamp(item) if event.rruleset is not None: # new recurring events should self._recalcCount += 1 self._eventsToAdd.clear() break elif (event.isBetween(start, end) and event.transparency == 'confirmed'): self._eventsToAdd.add(event) else: self._eventsToAdd.clear() if self._eventsToAdd: self._recalcCount += 1 if self._recalcCount or self._eventsToAdd: self.Refresh()
def updateFreebusyFromVObject(view, text, busyCollection, activity=None): """ Take a string, create or update freebusy events in busyCollection from that stream. Truncate differing existing freebusy events that overlap the start or end times. Returns (freebusystart, freebusyend, calname). """ newItemParent = view.findPath("//userdata") countNew = 0 countUpdated = 0 freebusystart = freebusyend = None Calendar.ensureIndexed(busyCollection) # iterate over calendars, usually only one, but more are allowed for calendar in vobject.readComponents(text, validate=True, ignoreUnreadable=True): calname = calendar.getChildValue("x_wr_calname") for vfreebusy in calendar.vfreebusy_list: # RPI's server originally didn't put a VERSION:2.0 line in its # freebusy response. vobject's behavior is set when a VERSION is # found. Tolerate servers that export technically illegal but still # readable vfreebusy components if vfreebusy.behavior is None: vfreebusy.behavior = vobject.icalendar.VFreeBusy vfreebusy.transformToNative() start = vfreebusy.getChildValue("dtstart") end = vfreebusy.getChildValue("dtend") if freebusystart is None or freebusystart > start: freebusystart = start if freebusyend is None or freebusyend < end: freebusyend = end # create a list of busy blocks tuples sorted by start time busyblocks = [] for fb in getattr(vfreebusy, "freebusy_list", []): status = getattr(fb, "fbtype_param", "BUSY").upper() for blockstart, duration in fb.value: blockstart = translateToTimezone(blockstart, view.tzinfo.default) bisect.insort(busyblocks, (blockstart, duration, status)) # eventsInRange sorts by start time, recurring events aren't allowed # so we don't bother to fetch them existing = Calendar.eventsInRange(view, start, end, busyCollection) existing = itertools.chain(existing, [None]) oldEvent = existing.next() for blockstart, duration, status in busyblocks: while oldEvent is not None and oldEvent.startTime < blockstart: # this assumes no freebusy blocks overlap freebusystart oldEvent.delete() oldEvent = existing.next() if oldEvent is not None and oldEvent.startTime == blockstart: oldEvent.transparency = reverseTransparencyMap[status] oldEvent.duration = duration countUpdated += 1 oldEvent = existing.next() else: vals = { "startTime": blockstart, "transparency": reverseTransparencyMap[status], "duration": duration, "isFreeBusy": True, "anyTime": False, "summary": "", } eventItem = CalendarEvent(None, newItemParent, **vals) busyCollection.add(eventItem.itsItem) countNew += 1 logger.info("...iCalendar import of %d new freebusy blocks, %d updated", countNew, countUpdated) return freebusystart, freebusyend, calname
def _doBusyCalculations(self): view = self.blockItem.itsView startDate = self.GetStartDate() endDate = minical.MonthDelta(startDate, 3) busyFractions = {} defaultTzinfo = view.tzinfo.default tzEnabled = schema.ns("osaf.pim", view).TimezonePrefs.showUI # The exact algorithm for the busy state is yet to be determined. # For now, just get the confirmed items on a given day and calculate # their total duration. As long as there is at least one event the # busy bar should be at least 1/4 height (so that it is visible). # A 100% full day is assumed to be 12 hours worth of appointments. def updateBusy(event, start): # Broken out into a separate function because we're going # to call it for each non-recurring events, and for each # individual occurrence of all the recurring events. # In the case of the latter, event may be the master, or # a modification; we're trying to avoid creating all the # items for individual computed occurrences. if event.transparency == "confirmed" and ( event.allDay or (not event.anyTime and event.duration != zero_delta) ): assert start.tzinfo is not None # If timezones are enabled, we need to convert to the # default tzinfo here, so that date() below refers to # the correct timezone. if tzEnabled: start = start.astimezone(defaultTzinfo) else: start = start.replace(tzinfo=defaultTzinfo) offset = (start.date() - startDate).days midnightStart = datetime.combine(start.date(), time(0, tzinfo=defaultTzinfo)) if event.allDay: days = event.duration.days + 1 else: days = (start + event.duration - midnightStart).days + 1 for day in xrange(days): if event.allDay: hours = 12.0 else: today = midnightStart + timedelta(day) dayStart = max(start, today) earliestEnd = max(start + event.duration, today) dayEnd = min(earliestEnd, today + one_day) duration = dayEnd - dayStart assert duration >= zero_delta hours = duration.seconds / 3600 + 24 * duration.days # We set a minimum "Busy" value of 0.25 for any # day with a confirmed event. fraction = busyFractions.get(offset + day, 0.0) fraction = max(fraction, 0.25) fraction += hours / 12.0 busyFractions[offset + day] = min(fraction, 1.0) if len(self._eventsToAdd) > 0: # First, set up busyFractions to contain the # existing values for all the dates of events # we're about to add for newEvent in self._eventsToAdd: newEventStartTimeDate = newEvent.startTime.date() offset = (newEventStartTimeDate - startDate).days busyFractions[offset] = self.GetBusy(newEventStartTimeDate) # Now, update them all for newEvent in self._eventsToAdd: updateBusy(newEvent, newEvent.startTime) # Finally, update the UI for offset, busy in busyFractions.iteritems(): eventDate = startDate + timedelta(days=offset) self.SetBusy(eventDate, busy) else: # Largely, this code is stolen from CalendarCanvas.py; it # would be good to refactor it at some point. self.blockItem.EnsureIndexes() startOfDay = time(0, tzinfo=view.tzinfo.default) startDatetime = datetime.combine(startDate, startOfDay) endDatetime = datetime.combine(endDate, startOfDay) events = self.blockItem.contents view = self.blockItem.itsView for event, start in Calendar.iterBusyInfo(view, startDatetime, endDatetime, events): updateBusy(event, start) # Next, try to find all generated events in the given # datetime range offset = 0 while startDate < endDate: self.SetBusy(startDate, busyFractions.get(offset, 0.0)) startDate += one_day offset += 1