def test_client_side_ordering_on_mixed_all_day_and_normal(self): # Test that client-side ordering on start and end fields works for items that are a mix of normal an all-day # items. This requires us to compare EWSDateTime -> EWSDate values which is not allowed by default (EWSDate -> # EWSDateTime *is* allowed). start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) all_day_date = (start - datetime.timedelta(days=1)).date() item1 = self.ITEM_CLASS( account=self.account, folder=self.test_folder, subject=get_random_string(16), start=all_day_date, end=all_day_date, is_all_day=True, categories=self.categories, ) item2 = self.ITEM_CLASS( account=self.account, folder=self.test_folder, subject=get_random_string(16), start=start, end=end, categories=self.categories, ) self.test_folder.bulk_create(items=[item1, item2]) list( self.test_folder.view(start=start - datetime.timedelta(days=1), end=end).order_by('start')) list( self.test_folder.view(start=start - datetime.timedelta(days=1), end=end).order_by('-start'))
def test_ewsdatetime(self): tz = EWSTimeZone.timezone('Europe/Copenhagen') self.assertIsInstance(tz, EWSTimeZone) self.assertEqual(tz.ms_id, 'Romance Standard Time') self.assertEqual(tz.ms_name, '(UTC+01:00) Brussels, Copenhagen, Madrid, Paris') dt = tz.localize(EWSDateTime(2000, 1, 2, 3, 4, 5)) self.assertIsInstance(dt, EWSDateTime) self.assertIsInstance(dt.tzinfo, EWSTimeZone) self.assertEqual(dt.tzinfo.ms_id, tz.ms_id) self.assertEqual(dt.tzinfo.ms_name, tz.ms_name) self.assertEqual(str(dt), '2000-01-02 03:04:05+01:00') self.assertEqual( repr(dt), "EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=<DstTzInfo 'Europe/Copenhagen' CET+1:00:00 STD>)" ) self.assertIsInstance(dt + timedelta(days=1), EWSDateTime) self.assertIsInstance(dt - timedelta(days=1), EWSDateTime) self.assertIsInstance(dt - EWSDateTime.now(tz=tz), timedelta) self.assertIsInstance(EWSDateTime.now(tz=tz), EWSDateTime) self.assertEqual(dt, EWSDateTime.from_datetime(tz.localize(datetime(2000, 1, 2, 3, 4, 5)))) self.assertEqual(dt.ewsformat(), '2000-01-02T03:04:05') utc_tz = EWSTimeZone.timezone('UTC') self.assertEqual(dt.astimezone(utc_tz).ewsformat(), '2000-01-02T02:04:05Z') # Test summertime dt = tz.localize(EWSDateTime(2000, 8, 2, 3, 4, 5)) self.assertEqual(dt.astimezone(utc_tz).ewsformat(), '2000-08-02T01:04:05Z')
def test_delete_occurrence_via_index(self): # Test deleting occurrences via occurrence index without knowing the ID of the occurrence. start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, subject=get_random_string(16), recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() # Delete the third occurrence third_occurrence = master_item.occurrence(index=3) third_occurrence.refresh() # Test that GetItem works third_occurrence = master_item.occurrence(index=3) third_occurrence.delete() # Test that DeleteItem works # Test change on the master item master_item.refresh() self.assertEqual(master_item.modified_occurrences, None) self.assertEqual(len(master_item.deleted_occurrences), 1) deleted_occurrence = master_item.deleted_occurrences[0] self.assertIsInstance(deleted_occurrence, DeletedOccurrence) self.assertEqual(deleted_occurrence.start, start + datetime.timedelta(days=2))
def test_task_validation(self): tz = self.account.default_timezone task = Task(due_date=EWSDate(2017, 1, 1), start_date=EWSDate(2017, 2, 1)) task.clean() # We reset due date if it's before start date self.assertEqual(task.due_date, EWSDate(2017, 2, 1)) self.assertEqual(task.due_date, task.start_date) task = Task(complete_date=EWSDateTime(2099, 1, 1, tzinfo=tz), status=Task.NOT_STARTED) task.clean() # We reset status if complete_date is set self.assertEqual(task.status, Task.COMPLETED) # We also reset complete date to now() if it's in the future self.assertEqual(task.complete_date.date(), UTC_NOW().date()) task = Task(complete_date=EWSDateTime(2017, 1, 1, tzinfo=tz), start_date=EWSDate(2017, 2, 1)) task.clean() # We also reset complete date to start_date if it's before start_date self.assertEqual(task.complete_date.date(), task.start_date) task = Task(percent_complete=Decimal('50.0'), status=Task.COMPLETED) task.clean() # We reset percent_complete to 100.0 if state is completed self.assertEqual(task.percent_complete, Decimal(100)) task = Task(percent_complete=Decimal('50.0'), status=Task.NOT_STARTED) task.clean() # We reset percent_complete to 0.0 if state is not_started self.assertEqual(task.percent_complete, Decimal(0))
def test_change_occurrence(self): # Test that we can make changes to individual occurrences and see the effect on the master item. start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() master_item.refresh() # Test occurrences as full calendar items, unfolded from the master range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] # Change the start and end of the second occurrence second_occurrence = unfolded[1] second_occurrence.start += datetime.timedelta(hours=1) second_occurrence.end += datetime.timedelta(hours=1) second_occurrence.save() # Test change on the master item master_item.refresh() self.assertEqual(len(master_item.modified_occurrences), 1) modified_occurrence = master_item.modified_occurrences[0] self.assertIsInstance(modified_occurrence, Occurrence) self.assertEqual(modified_occurrence.id, second_occurrence.id) self.assertEqual(modified_occurrence.start, second_occurrence.start) self.assertEqual(modified_occurrence.end, second_occurrence.end) self.assertEqual(modified_occurrence.original_start, second_occurrence.start - datetime.timedelta(hours=1)) self.assertEqual(master_item.deleted_occurrences, None) # Test change on the unfolded item unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] self.assertEqual(len(unfolded), 4) self.assertEqual(unfolded[1].type, EXCEPTION) self.assertEqual(unfolded[1].start, second_occurrence.start) self.assertEqual(unfolded[1].end, second_occurrence.end) self.assertEqual(unfolded[1].original_start, second_occurrence.start - datetime.timedelta(hours=1))
def get_random_datetime(start_date=EWSDate(1996, 1, 1), end_date=EWSDate(2030, 1, 1), tz=UTC): # Create a random datetime with minute precision. Both dates are inclusive. # Keep with a reasonable date range. A wider date range than the default values is unstable WRT timezones. random_date = get_random_date(start_date=start_date, end_date=end_date) random_datetime = datetime.datetime.combine(random_date, datetime.time.min) \ + datetime.timedelta(minutes=random.randint(0, 60 * 24)) return EWSDateTime.from_datetime(random_datetime).replace(tzinfo=tz)
def test_change_occurrence_via_index(self): # Test updating occurrences via occurrence index without knowing the ID of the occurrence. start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, subject=get_random_string(16), recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() # Change the start and end of the second occurrence second_occurrence = master_item.occurrence(index=2) second_occurrence.start = start + datetime.timedelta(days=1, hours=1) second_occurrence.end = end + datetime.timedelta(days=1, hours=1) second_occurrence.save(update_fields=[ 'start', 'end' ]) # Test that UpdateItem works with only a few fields second_occurrence = master_item.occurrence(index=2) second_occurrence.refresh() self.assertEqual(second_occurrence.subject, master_item.subject) second_occurrence.start += datetime.timedelta(hours=1) second_occurrence.end += datetime.timedelta(hours=1) second_occurrence.save( update_fields=['start', 'end']) # Test that UpdateItem works after refresh # Test change on the master item master_item.refresh() self.assertEqual(len(master_item.modified_occurrences), 1) modified_occurrence = master_item.modified_occurrences[0] self.assertIsInstance(modified_occurrence, Occurrence) self.assertEqual(modified_occurrence.id, second_occurrence.id) self.assertEqual(modified_occurrence.start, second_occurrence.start) self.assertEqual(modified_occurrence.end, second_occurrence.end) self.assertEqual(modified_occurrence.original_start, second_occurrence.start - datetime.timedelta(hours=2)) self.assertEqual(master_item.deleted_occurrences, None)
def test_get_master_recurrence(self): # Test getting the master recurrence via an occurrence start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) recurrence = Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, subject=get_random_string(16), recurrence=recurrence, categories=self.categories, ).save() # Get the master from an occurrence range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] third_occurrence = unfolded[2] self.assertEqual(third_occurrence.recurrence, None) master_from_occurrence = third_occurrence.recurring_master() master_from_occurrence.refresh() # Test that GetItem works self.assertEqual(master_from_occurrence.recurrence, recurrence) self.assertEqual(master_from_occurrence.subject, master_item.subject) master_from_occurrence = third_occurrence.recurring_master() master_from_occurrence.subject = get_random_string(16) master_from_occurrence.save( update_fields=['subject']) # Test that UpdateItem works master_from_occurrence.delete() # Test that DeleteItem works with self.assertRaises(ErrorItemNotFound): master_item.delete( ) # Item is gone from the server, so this should fail with self.assertRaises(ErrorItemNotFound): third_occurrence.delete( ) # Item is gone from the server, so this should fail
def test_delete_occurrence(self): # Test that we can delete an occurrence and see the cange on the master item start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() master_item.refresh() # Test occurrences as full calendar items, unfolded from the master range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] # Delete the third occurrence third_occurrence = unfolded[2] third_occurrence.delete() # Test change on the master item master_item.refresh() self.assertEqual(master_item.modified_occurrences, None) self.assertEqual(len(master_item.deleted_occurrences), 1) deleted_occurrence = master_item.deleted_occurrences[0] self.assertIsInstance(deleted_occurrence, DeletedOccurrence) self.assertEqual(deleted_occurrence.start, third_occurrence.start) # Test change on the unfolded items unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] self.assertEqual(len(unfolded), 3)
def get_random_datetime(start_date=EWSDate(1996, 1, 1), end_date=EWSDate(2030, 1, 1), tz=UTC): # Create a random datetime with minute precision. Both dates are inclusive. # Keep with a reasonable date range. A wider date range than the default values is unstable WRT timezones. while True: try: random_date = get_random_date(start_date=start_date, end_date=end_date) random_datetime = datetime.datetime.combine(random_date, datetime.time.min) \ + datetime.timedelta(minutes=random.randint(0, 60 * 24)) return tz.localize(EWSDateTime.from_datetime(random_datetime), is_dst=None) except (AmbiguousTimeError, NonExistentTimeError): pass
def test_task_validation(self): tz = EWSTimeZone.timezone('Europe/Copenhagen') task = Task(due_date=tz.localize(EWSDateTime(2017, 1, 1)), start_date=tz.localize(EWSDateTime(2017, 2, 1))) task.clean() # We reset due date if it's before start date self.assertEqual(task.due_date, tz.localize(EWSDateTime(2017, 2, 1))) self.assertEqual(task.due_date, task.start_date) task = Task(complete_date=tz.localize(EWSDateTime(2099, 1, 1)), status=Task.NOT_STARTED) task.clean() # We reset status if complete_date is set self.assertEqual(task.status, Task.COMPLETED) # We also reset complete date to now() if it's in the future self.assertEqual(task.complete_date.date(), UTC_NOW().date()) task = Task(complete_date=tz.localize(EWSDateTime(2017, 1, 1)), start_date=tz.localize(EWSDateTime(2017, 2, 1))) task.clean() # We also reset complete date to start_date if it's before start_date self.assertEqual(task.complete_date, task.start_date) task = Task(percent_complete=Decimal('50.0'), status=Task.COMPLETED) task.clean() # We reset percent_complete to 100.0 if state is completed self.assertEqual(task.percent_complete, Decimal(100)) task = Task(percent_complete=Decimal('50.0'), status=Task.NOT_STARTED) task.clean() # We reset percent_complete to 0.0 if state is not_started self.assertEqual(task.percent_complete, Decimal(0))
def test_localize(self): # Test some corner cases around DST tz = EWSTimeZone('Europe/Copenhagen') with warnings.catch_warnings(): # localize() is deprecated but we still want to test it. Silence the DeprecationWarning warnings.simplefilter("ignore") self.assertEqual( str( tz.localize(EWSDateTime(2023, 10, 29, 2, 36, 0), is_dst=False)), '2023-10-29 02:36:00+01:00') self.assertEqual( str( tz.localize(EWSDateTime(2023, 10, 29, 2, 36, 0), is_dst=None)), '2023-10-29 02:36:00+02:00') self.assertEqual( str( tz.localize(EWSDateTime(2023, 10, 29, 2, 36, 0), is_dst=True)), '2023-10-29 02:36:00+02:00') self.assertEqual( str( tz.localize(EWSDateTime(2023, 3, 26, 2, 36, 0), is_dst=False)), '2023-03-26 02:36:00+01:00') self.assertEqual( str( tz.localize(EWSDateTime(2023, 3, 26, 2, 36, 0), is_dst=None)), '2023-03-26 02:36:00+01:00') self.assertEqual( str( tz.localize(EWSDateTime(2023, 3, 26, 2, 36, 0), is_dst=True)), '2023-03-26 02:36:00+02:00')
def test_super_methods(self): tz = EWSTimeZone('Europe/Copenhagen') self.assertIsInstance(EWSDateTime.now(), EWSDateTime) self.assertIsInstance(EWSDateTime.now(tz=tz), EWSDateTime) self.assertIsInstance(EWSDateTime.utcnow(), EWSDateTime) self.assertIsInstance(EWSDateTime.fromtimestamp(123456789), EWSDateTime) self.assertIsInstance(EWSDateTime.fromtimestamp(123456789, tz=tz), EWSDateTime) self.assertIsInstance(EWSDateTime.utcfromtimestamp(123456789), EWSDateTime)
def test_ewsdatetime(self): # Test a static timezone tz = EWSTimeZone('Etc/GMT-5') dt = EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=tz) self.assertIsInstance(dt, EWSDateTime) self.assertIsInstance(dt.tzinfo, EWSTimeZone) self.assertEqual(dt.tzinfo.ms_id, tz.ms_id) self.assertEqual(dt.tzinfo.ms_name, tz.ms_name) self.assertEqual(str(dt), '2000-01-02 03:04:05+05:00') self.assertEqual( repr(dt), "EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=EWSTimeZone(key='Etc/GMT-5'))" ) # Test a DST timezone tz = EWSTimeZone('Europe/Copenhagen') dt = EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=tz) self.assertIsInstance(dt, EWSDateTime) self.assertIsInstance(dt.tzinfo, EWSTimeZone) self.assertEqual(dt.tzinfo.ms_id, tz.ms_id) self.assertEqual(dt.tzinfo.ms_name, tz.ms_name) self.assertEqual(str(dt), '2000-01-02 03:04:05+01:00') self.assertEqual( repr(dt), "EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=EWSTimeZone(key='Europe/Copenhagen'))" ) # Test from_string with self.assertRaises(NaiveDateTimeNotAllowed): EWSDateTime.from_string('2000-01-02T03:04:05') self.assertEqual(EWSDateTime.from_string('2000-01-02T03:04:05+01:00'), EWSDateTime(2000, 1, 2, 2, 4, 5, tzinfo=UTC)) self.assertEqual(EWSDateTime.from_string('2000-01-02T03:04:05Z'), EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=UTC)) self.assertIsInstance( EWSDateTime.from_string('2000-01-02T03:04:05+01:00'), EWSDateTime) self.assertIsInstance(EWSDateTime.from_string('2000-01-02T03:04:05Z'), EWSDateTime) # Test addition, subtraction, summertime etc self.assertIsInstance(dt + datetime.timedelta(days=1), EWSDateTime) self.assertIsInstance(dt - datetime.timedelta(days=1), EWSDateTime) self.assertIsInstance(dt - EWSDateTime.now(tz=tz), datetime.timedelta) self.assertIsInstance(EWSDateTime.now(tz=tz), EWSDateTime) # Test various input for from_datetime() self.assertEqual( dt, EWSDateTime.from_datetime( datetime.datetime(2000, 1, 2, 3, 4, 5, tzinfo=EWSTimeZone('Europe/Copenhagen')))) self.assertEqual( dt, EWSDateTime.from_datetime( datetime.datetime( 2000, 1, 2, 3, 4, 5, tzinfo=zoneinfo.ZoneInfo('Europe/Copenhagen')))) self.assertEqual( dt, EWSDateTime.from_datetime( datetime.datetime( 2000, 1, 2, 3, 4, 5, tzinfo=dateutil.tz.gettz('Europe/Copenhagen')))) self.assertEqual( dt, EWSDateTime.from_datetime( datetime.datetime(2000, 1, 2, 3, 4, 5, tzinfo=pytz.timezone('Europe/Copenhagen')))) self.assertEqual(dt.ewsformat(), '2000-01-02T03:04:05+01:00') utc_tz = EWSTimeZone('UTC') self.assertEqual( dt.astimezone(utc_tz).ewsformat(), '2000-01-02T02:04:05Z') # Test summertime dt = EWSDateTime(2000, 8, 2, 3, 4, 5, tzinfo=tz) self.assertEqual( dt.astimezone(utc_tz).ewsformat(), '2000-08-02T01:04:05Z') # Test in-place add and subtract dt = EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=tz) dt += datetime.timedelta(days=1) self.assertIsInstance(dt, EWSDateTime) self.assertEqual(dt, EWSDateTime(2000, 1, 3, 3, 4, 5, tzinfo=tz)) dt = EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=tz) dt -= datetime.timedelta(days=1) self.assertIsInstance(dt, EWSDateTime) self.assertEqual(dt, EWSDateTime(2000, 1, 1, 3, 4, 5, tzinfo=tz)) # Test ewsformat() failure dt = EWSDateTime(2000, 1, 2, 3, 4, 5) with self.assertRaises(ValueError): dt.ewsformat() # Test wrong tzinfo type with self.assertRaises(ValueError): EWSDateTime(2000, 1, 2, 3, 4, 5, tzinfo=pytz.timezone('Europe/Copenhagen')) with self.assertRaises(ValueError): EWSDateTime.from_datetime(EWSDateTime(2000, 1, 2, 3, 4, 5))
def test_view(self): item1 = self.ITEM_CLASS( account=self.account, folder=self.test_folder, subject=get_random_string(16), start=EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone), end=EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone), categories=self.categories, ) item2 = self.ITEM_CLASS( account=self.account, folder=self.test_folder, subject=get_random_string(16), start=EWSDateTime(2016, 2, 1, 8, tzinfo=self.account.default_timezone), end=EWSDateTime(2016, 2, 1, 10, tzinfo=self.account.default_timezone), categories=self.categories, ) self.test_folder.bulk_create(items=[item1, item2]) qs = self.test_folder.view(start=item1.start, end=item2.end) # Test missing args with self.assertRaises(TypeError): self.test_folder.view() # Test bad args with self.assertRaises(ValueError): list(self.test_folder.view(start=item1.end, end=item1.start)) with self.assertRaises(TypeError): list(self.test_folder.view(start='xxx', end=item1.end)) with self.assertRaises(ValueError): list( self.test_folder.view(start=item1.start, end=item1.end, max_items=0)) # Test dates self.assertEqual( len([ i for i in self.test_folder.view(start=item1.start, end=item1.end) if self.match_cat(i) ]), 1) self.assertEqual( len([ i for i in self.test_folder.view(start=item1.start, end=item2.end) if self.match_cat(i) ]), 2) # Edge cases. Get view from end of item1 to start of item2. Should logically return 0 items, but Exchange wants # it differently and returns item1 even though there is no overlap. self.assertEqual( len([ i for i in self.test_folder.view(start=item1.end, end=item2.start) if self.match_cat(i) ]), 1) self.assertEqual( len([ i for i in self.test_folder.view(start=item1.start, end=item2.start) if self.match_cat(i) ]), 1) # Test max_items self.assertEqual( len([ i for i in self.test_folder.view( start=item1.start, end=item2.end, max_items=9999) if self.match_cat(i) ]), 2) self.assertEqual( self.test_folder.view(start=item1.start, end=item2.end, max_items=1).count(), 1) # Test client-side ordering self.assertListEqual( [i.subject for i in qs.order_by('subject') if self.match_cat(i)], sorted([item1.subject, item2.subject])) # Test client-side ordering on a field with no default value and no default value_cls value self.assertListEqual( [i.start for i in qs.order_by('-start') if self.match_cat(i)], [item2.start, item1.start]) # Test client-side ordering on multiple fields. Intentionally sort first on a field where values are equal, # to see that we can sort on the 2nd field. self.assertListEqual([ i.start for i in qs.order_by('categories', '-start') if self.match_cat(i) ], [item2.start, item1.start]) # Test chaining self.assertTrue(qs.count() >= 2) with self.assertRaises(ErrorInvalidOperation): qs.filter(subject=item1.subject).count( ) # EWS does not allow restrictions self.assertListEqual([ i for i in qs.order_by('subject').values('subject') if i['subject'] in (item1.subject, item2.subject) ], [{ 'subject': s } for s in sorted([item1.subject, item2.subject])])
def get_random_datetime(start_date=date(1900, 1, 1), end_date=date(2100, 1, 1)): # Create a random datetime with minute precision random_date = get_random_date(start_date=start_date, end_date=end_date) random_datetime = datetime.combine(random_date, time.min) + timedelta(minutes=random.randint(0, 60*24)) return UTC.localize(EWSDateTime.from_datetime(random_datetime))
def addEvent(calendarName, eventName, eventDescription, eventStartDate, eventEndDate): """ Add a new event to a calendar eventDescription can be empty eventDuration is calculated later by using eventStartDate and eventEndDate calendarName is stored as an Outlook calendar category """ conn, cursor, tz, account = initEventStorage() # calendarName in lowercase to handle problems with finding it in database calendarName = calendarName.lower() if not checkIfCalendarExists(calendarName): return False, "Calendar {0} does not exist".format(calendarName) if not eventName: return False, "Event name is empty" try: convEventStartDate = datetime.datetime.strptime( eventStartDate, "%d.%m.%Y %H:%M") except ValueError: return False, "Event start date {0} could not be converted to date, Date format should be: %d.%m.%Y %H:%M".format( eventStartDate) try: convEventEndDate = datetime.datetime.strptime(eventEndDate, "%d.%m.%Y %H:%M") except ValueError: return False, "Event end date {0} could not be converted to date, Date format should be: %d.%m.%Y %H:%M".format( eventEndDate) timeDuration = convEventEndDate - convEventStartDate if timeDuration < datetime.timedelta(0): return False, "Event duration is negative, please check start date {0} and end date {1}".format( convEventStartDate, convEventEndDate) eventDuration = str(timeDuration) # Add event to sqlite calendar addEventToSQLCalendar = "INSERT INTO '{0}' (eventName, eventDescription, eventStartDate, eventEndDate, eventDuration) VALUES ('{1}', '{2}', '{3}', '{4}', '{5}')".format( calendarName, eventName, eventDescription, convEventStartDate, convEventEndDate, eventDuration) conn.execute(addEventToSQLCalendar) conn.commit() # Create and add event to outlook calendar # A list of calendar items is required, so let us create one. calendar_items = [] calendar_items.append( CalendarItem( start=tz.localize( EWSDateTime(convEventStartDate.year, convEventStartDate.month, convEventStartDate.day, convEventStartDate.hour, convEventStartDate.minute, convEventStartDate.second, convEventStartDate.microsecond)), end=tz.localize( EWSDateTime(convEventEndDate.year, convEventEndDate.month, convEventEndDate.day, convEventEndDate.hour, convEventEndDate.minute, convEventEndDate.second, convEventEndDate.microsecond)), subject=eventName, body=eventDescription, location='empty', categories=[calendarName], )) res = account.calendar.add_items(calendar_items) return True, "Event {0} created".format(eventName)
def showAgenda(calendarName='', eventStartDate='', eventEndDate='', deleteAfterwards=False): """ Shows Agenda using the calendar in Outlook If no calendarName is given which is used as a category all events are shown if calendarName is given only events of this category are shown if start and end date are given the agenda of this time span is shown, otherwise agenda of today is shown. """ conn, cursor, tz, account = initEventStorage() # calendarName in lowercase to handle problems with finding it in database calendarName = calendarName.lower() # Strip whitespace calendarName = calendarName.strip() # Check if calendar exists otherwise, we cannot show its agenda if calendarName == '': calendarCategory = None elif checkIfCalendarExists(calendarName): calendarCategory = [calendarName] else: return False, "Calendar {0} does not exist, agenda cannot be shown. Try command without stating a calendar".format( calendarName) # If start and end date is given check if it is a positive time span if eventStartDate or eventEndDate: try: convEventStartDate = datetime.datetime.strptime( eventStartDate, "%d.%m.%Y %H:%M") except ValueError: try: convEventStartDate = datetime.datetime.strptime( eventStartDate, "%d.%m.%Y") convEventStartDate = convEventStartDate.replace(hour=0, minute=00) except ValueError: return False, "Event start date {0} could not be converted to date, Date format should be either: %d.%m.%Y or: %d.%m.%Y %H:%M".format( eventStartDate) try: convEventEndDate = datetime.datetime.strptime( eventEndDate, "%d.%m.%Y %H:%M") except ValueError: try: convEventEndDate = datetime.datetime.strptime( eventEndDate, "%d.%m.%Y") convEventEndDate = convEventEndDate.replace(hour=23, minute=59) except ValueError: return False, "Event end date {0} could not be converted to date, Date format should be either: %d.%m.%Y or: %d.%m.%Y %H:%M".format( eventEndDate) timeDuration = convEventEndDate - convEventStartDate if timeDuration < datetime.timedelta(0): return False, "Event duration is negative, please check start date {0} and end date {1}".format( convEventStartDate, convEventEndDate) eventDuration = str(timeDuration) # use today for the agenda else: nowDate = datetime.datetime.now() convEventStartDate = nowDate.replace(hour=0, minute=00) convEventEndDate = nowDate.replace(hour=23, minute=59) # Get Exchange ID and of the calendar items try: startDate = tz.localize( EWSDateTime(convEventStartDate.year, convEventStartDate.month, convEventStartDate.day, convEventStartDate.hour, convEventStartDate.minute)) endDate = tz.localize( EWSDateTime(convEventEndDate.year, convEventEndDate.month, convEventEndDate.day, convEventEndDate.hour, convEventEndDate.minute)) ids = account.calendar.find_items( start=startDate, end=endDate, categories=calendarCategory, shape=IdOnly, ) # Get full selected items items = account.calendar.get_items(ids) # Delete selected items after they have been shown in calendarBot if deleteAfterwards: res = account.calendar.delete_items(ids) except Exception as e: return False, "Could not access event items in Outlook calendar due to following exception:\n {0} \n {1}".format( e.__doc__, e.message) return True, items
def test_recurring_item(self): # Create a recurring calendar item. Test that occurrence fields are correct on the master item # Create a master item with 4 daily occurrences from 8:00 to 10:00. 'start' and 'end' are values for the first # occurrence. start = EWSDateTime(2016, 1, 1, 8, tzinfo=self.account.default_timezone) end = EWSDateTime(2016, 1, 1, 10, tzinfo=self.account.default_timezone) master_item = self.ITEM_CLASS( folder=self.test_folder, start=start, end=end, recurrence=Recurrence(pattern=DailyPattern(interval=1), start=start.date(), number=4), categories=self.categories, ).save() master_item.refresh() self.assertEqual(master_item.is_recurring, False) self.assertEqual(master_item.type, RECURRING_MASTER) self.assertIsInstance(master_item.first_occurrence, FirstOccurrence) self.assertEqual(master_item.first_occurrence.start, start) self.assertEqual(master_item.first_occurrence.end, end) self.assertIsInstance(master_item.last_occurrence, LastOccurrence) self.assertEqual(master_item.last_occurrence.start, start + datetime.timedelta(days=3)) self.assertEqual(master_item.last_occurrence.end, end + datetime.timedelta(days=3)) self.assertEqual(master_item.modified_occurrences, None) self.assertEqual(master_item.deleted_occurrences, None) # Test occurrences as full calendar items, unfolded from the master range_start, range_end = start, end + datetime.timedelta(days=3) unfolded = [ i for i in self.test_folder.view(start=range_start, end=range_end) if self.match_cat(i) ] self.assertEqual(len(unfolded), 4) for item in unfolded: self.assertEqual(item.type, OCCURRENCE) self.assertEqual(item.is_recurring, True) first_occurrence = unfolded[0] self.assertEqual(first_occurrence.id, master_item.first_occurrence.id) self.assertEqual(first_occurrence.start, master_item.first_occurrence.start) self.assertEqual(first_occurrence.end, master_item.first_occurrence.end) second_occurrence = unfolded[1] self.assertEqual(second_occurrence.start, master_item.start + datetime.timedelta(days=1)) self.assertEqual(second_occurrence.end, master_item.end + datetime.timedelta(days=1)) third_occurrence = unfolded[2] self.assertEqual(third_occurrence.start, master_item.start + datetime.timedelta(days=2)) self.assertEqual(third_occurrence.end, master_item.end + datetime.timedelta(days=2)) last_occurrence = unfolded[3] self.assertEqual(last_occurrence.id, master_item.last_occurrence.id) self.assertEqual(last_occurrence.start, master_item.last_occurrence.start) self.assertEqual(last_occurrence.end, master_item.last_occurrence.end)
def test_recurring_item(self): """Changes to an occurrence of a recurring task cause one-off tasks to be generated when the following updates are made: * The status property of a regenerating or nonregenerating recurrent task is set to Completed. * The start date or end date of a nonregenerating recurrent task is changed. """ # Create a master non-regenerating item with 4 daily occurrences start = EWSDate(2016, 1, 1) recurrence = TaskRecurrence(pattern=DailyPattern(interval=1), start=start, number=4) nonregenerating_item = self.ITEM_CLASS( folder=self.test_folder, categories=self.categories, recurrence=recurrence, ).save() nonregenerating_item.refresh() master_item_id = nonregenerating_item.id self.assertEqual(nonregenerating_item.is_recurring, True) self.assertEqual(nonregenerating_item.change_count, 1) self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 1) # Change the start date. We should see a new task appear. master_item = self.get_item_by_id((master_item_id, None)) master_item.recurrence.boundary.start = EWSDate(2016, 2, 1) occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.is_recurring, False) # This is now the occurrence self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 2) # Check fields on the recurring item master_item = self.get_item_by_id((master_item_id, None)) self.assertEqual(master_item.change_count, 2) self.assertEqual(master_item.due_date, EWSDate(2016, 1, 2)) # This is the next occurrence self.assertEqual(master_item.recurrence.boundary.number, 3) # One less # Change the status to 'Completed'. We should see a new task appear. master_item.status = Task.COMPLETED occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.is_recurring, False) # This is now the occurrence self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 3) # Check fields on the recurring item master_item = self.get_item_by_id((master_item_id, None)) self.assertEqual(master_item.change_count, 3) self.assertEqual(master_item.due_date, EWSDate(2016, 2, 1)) # This is the next occurrence self.assertEqual(master_item.recurrence.boundary.number, 2) # One less self.test_folder.filter(categories__contains=self.categories).delete() # Create a master regenerating item with 4 daily occurrences recurrence = TaskRecurrence(pattern=DailyRegeneration(interval=1), start=start, number=4) regenerating_item = self.ITEM_CLASS( folder=self.test_folder, categories=self.categories, recurrence=recurrence, ).save() regenerating_item.refresh() master_item_id = regenerating_item.id self.assertEqual(regenerating_item.is_recurring, True) self.assertEqual(regenerating_item.change_count, 1) self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 1) # Change the start date. We should *not* see a new task appear. master_item = self.get_item_by_id((master_item_id, None)) master_item.recurrence.boundary.start = EWSDate(2016, 1, 2) occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.id, master_item.id) # This is not an occurrence. No new task was created self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 1) # Change the status to 'Completed'. We should see a new task appear. master_item.status = Task.COMPLETED occurrence_item = master_item.save() occurrence_item.refresh() self.assertEqual(occurrence_item.is_recurring, False) # This is now the occurrence self.assertEqual(self.test_folder.filter(categories__contains=self.categories).count(), 2) # Check fields on the recurring item master_item = self.get_item_by_id((master_item_id, None)) self.assertEqual(master_item.change_count, 2) # The due date is the next occurrence after today tz = self.account.default_timezone self.assertEqual(master_item.due_date, EWSDateTime.now(tz).date() + datetime.timedelta(days=1)) self.assertEqual(master_item.recurrence.boundary.number, 3) # One less