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_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_finditems(self): now = UTC_NOW() # Test argument types item = self.get_test_item() ids = self.test_folder.bulk_create(items=[item]) # No arguments. There may be leftover items in the folder, so just make sure there's at least one. self.assertGreaterEqual(self.test_folder.filter().count(), 1) # Q object self.assertEqual( self.test_folder.filter(Q(subject=item.subject)).count(), 1) # Multiple Q objects self.assertEqual( self.test_folder.filter( Q(subject=item.subject), ~Q(subject=item.subject[:-3] + 'XXX')).count(), 1) # Multiple Q object and kwargs self.assertEqual( self.test_folder.filter( Q(subject=item.subject), categories__contains=item.categories).count(), 1) self.bulk_delete(ids) # Test categories which are handled specially - only '__contains' and '__in' lookups are supported item = self.get_test_item(categories=['TestA', 'TestB']) ids = self.test_folder.bulk_create(items=[item]) common_qs = self.test_folder.filter( subject=item.subject) # Guard against other simultaneous runs self.assertEqual( common_qs.filter( categories__contains='ci6xahH1').count(), # Plain string 0) self.assertEqual( common_qs.filter(categories__contains=['ci6xahH1']).count( ), # Same, but as list 0) self.assertEqual( common_qs.filter(categories__contains=['TestA', 'TestC']).count( ), # One wrong category 0) self.assertEqual( common_qs.filter(categories__contains=['TESTA']).count( ), # Test case insensitivity 1) self.assertEqual( common_qs.filter(categories__contains=['testa']).count( ), # Test case insensitivity 1) self.assertEqual( common_qs.filter( categories__contains=['TestA']).count(), # Partial 1) self.assertEqual( common_qs.filter( categories__contains=item.categories).count(), # Exact match 1) with self.assertRaises(ValueError): common_qs.filter(categories__in='ci6xahH1').count( ) # Plain string is not supported self.assertEqual( common_qs.filter( categories__in=['ci6xahH1']).count(), # Same, but as list 0) self.assertEqual( common_qs.filter(categories__in=['TestA', 'TestC' ]).count(), # One wrong category 1) self.assertEqual( common_qs.filter(categories__in=['TestA']).count(), # Partial 1) self.assertEqual( common_qs.filter( categories__in=item.categories).count(), # Exact match 1) self.bulk_delete(ids) common_qs = self.test_folder.filter( categories__contains=self.categories) one_hour = datetime.timedelta(hours=1) two_hours = datetime.timedelta(hours=2) # Test 'exists' ids = self.test_folder.bulk_create(items=[self.get_test_item()]) self.assertEqual( common_qs.filter(datetime_created__exists=True).count(), 1) self.assertEqual( common_qs.filter(datetime_created__exists=False).count(), 0) self.bulk_delete(ids) # Test 'range' ids = self.test_folder.bulk_create(items=[self.get_test_item()]) self.assertEqual( common_qs.filter(datetime_created__range=(now + one_hour, now + two_hours)).count(), 0) self.assertEqual( common_qs.filter(datetime_created__range=(now - one_hour, now + one_hour)).count(), 1) self.bulk_delete(ids) # Test '>' ids = self.test_folder.bulk_create(items=[self.get_test_item()]) self.assertEqual( common_qs.filter(datetime_created__gt=now + one_hour).count(), 0) self.assertEqual( common_qs.filter(datetime_created__gt=now - one_hour).count(), 1) self.bulk_delete(ids) # Test '>=' ids = self.test_folder.bulk_create(items=[self.get_test_item()]) self.assertEqual( common_qs.filter(datetime_created__gte=now + one_hour).count(), 0) self.assertEqual( common_qs.filter(datetime_created__gte=now - one_hour).count(), 1) self.bulk_delete(ids) # Test '<' ids = self.test_folder.bulk_create(items=[self.get_test_item()]) self.assertEqual( common_qs.filter(datetime_created__lt=now - one_hour).count(), 0) self.assertEqual( common_qs.filter(datetime_created__lt=now + one_hour).count(), 1) self.bulk_delete(ids) # Test '<=' ids = self.test_folder.bulk_create(items=[self.get_test_item()]) self.assertEqual( common_qs.filter(datetime_created__lte=now - one_hour).count(), 0) self.assertEqual( common_qs.filter(datetime_created__lte=now + one_hour).count(), 1) self.bulk_delete(ids) # Test '=' item = self.get_test_item() ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject=item.subject[:-3] + 'XXX').count(), 0) self.assertEqual(common_qs.filter(subject=item.subject).count(), 1) self.bulk_delete(ids) # Test '!=' item = self.get_test_item() ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject__not=item.subject).count(), 0) self.assertEqual( common_qs.filter(subject__not=item.subject[:-3] + 'XXX').count(), 1) self.bulk_delete(ids) # Test 'exact' item = self.get_test_item() item.subject = 'aA' + item.subject[2:] ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject__exact=item.subject[:-3] + 'XXX').count(), 0) self.assertEqual( common_qs.filter(subject__exact=item.subject.lower()).count(), 0) self.assertEqual( common_qs.filter(subject__exact=item.subject.upper()).count(), 0) self.assertEqual( common_qs.filter(subject__exact=item.subject).count(), 1) self.bulk_delete(ids) # Test 'iexact' item = self.get_test_item() item.subject = 'aA' + item.subject[2:] ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject__iexact=item.subject[:-3] + 'XXX').count(), 0) self.assertIn( common_qs.filter(subject__iexact=item.subject.lower()).count(), (0, 1) # iexact search is broken on some EWS versions ) self.assertIn( common_qs.filter(subject__iexact=item.subject.upper()).count(), (0, 1) # iexact search is broken on some EWS versions ) self.assertEqual( common_qs.filter(subject__iexact=item.subject).count(), 1) self.bulk_delete(ids) # Test 'contains' item = self.get_test_item() item.subject = item.subject[2:8] + 'aA' + item.subject[8:] ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject__contains=item.subject[2:14] + 'XXX').count(), 0) self.assertEqual( common_qs.filter( subject__contains=item.subject[2:14].lower()).count(), 0) self.assertEqual( common_qs.filter( subject__contains=item.subject[2:14].upper()).count(), 0) self.assertEqual( common_qs.filter(subject__contains=item.subject[2:14]).count(), 1) self.bulk_delete(ids) # Test 'icontains' item = self.get_test_item() item.subject = item.subject[2:8] + 'aA' + item.subject[8:] ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject__icontains=item.subject[2:14] + 'XXX').count(), 0) self.assertIn( common_qs.filter( subject__icontains=item.subject[2:14].lower()).count(), (0, 1) # icontains search is broken on some EWS versions ) self.assertIn( common_qs.filter( subject__icontains=item.subject[2:14].upper()).count(), (0, 1) # icontains search is broken on some EWS versions ) self.assertEqual( common_qs.filter(subject__icontains=item.subject[2:14]).count(), 1) self.bulk_delete(ids) # Test 'startswith' item = self.get_test_item() item.subject = 'aA' + item.subject[2:] ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject__startswith='XXX' + item.subject[:12]).count(), 0) self.assertEqual( common_qs.filter( subject__startswith=item.subject[:12].lower()).count(), 0) self.assertEqual( common_qs.filter( subject__startswith=item.subject[:12].upper()).count(), 0) self.assertEqual( common_qs.filter(subject__startswith=item.subject[:12]).count(), 1) self.bulk_delete(ids) # Test 'istartswith' item = self.get_test_item() item.subject = 'aA' + item.subject[2:] ids = self.test_folder.bulk_create(items=[item]) self.assertEqual( common_qs.filter(subject__istartswith='XXX' + item.subject[:12]).count(), 0) self.assertIn( common_qs.filter( subject__istartswith=item.subject[:12].lower()).count(), (0, 1) # istartswith search is broken on some EWS versions ) self.assertIn( common_qs.filter( subject__istartswith=item.subject[:12].upper()).count(), (0, 1) # istartswith search is broken on some EWS versions ) self.assertEqual( common_qs.filter(subject__istartswith=item.subject[:12]).count(), 1) self.bulk_delete(ids)
# Use notify-send for email notifications and zenity for calendar notifications notify = sh.Command('/usr/bin/notify-send') zenity = sh.Command('/usr/bin/zenity') # Get the local timezone timedatectl = sh.Command('/usr/bin/timedatectl') for l in timedatectl(): if 'Timezone' in l: tz_name = l.split()[1] break else: raise ValueError('Timezone not found') tz = EWSTimeZone.timezone(tz_name) sleep = int(sys.argv[1]) # 1st arg to this script is the number of seconds to look back in the inbox now = UTC_NOW() emails_since = now - timedelta(seconds=sleep) cal_items_before = now + timedelta(seconds=sleep * 4) # Longer notice of upcoming appointments than new emails username, _, password = netrc().authenticators('office365') c = Credentials(username, password, is_service_account=False) a = Account(primary_smtp_address=c.username, credentials=c, access_type=DELEGATE, autodiscover=True, verify_ssl=False) for msg in a.calendar.view(start=now, end=cal_items_before)\ .only('start', 'end', 'subject', 'location')\ .order_by('start', 'end'): if msg.start < now: continue minutes_to_appointment = int((msg.start - now).total_seconds() / 60) subj = 'You have a meeting in %s minutes' % minutes_to_appointment body = '%s-%s: %s\n%s' % ( msg.start.astimezone(tz).strftime('%H:%M'),
def get_random_update_kwargs(self, item, insert_kwargs): update_kwargs = {} now = UTC_NOW() for f in self.ITEM_CLASS.FIELDS: if not f.supports_version(self.account.version): # Cannot be used with this EWS version continue if self.ITEM_CLASS == CalendarItem and f in CalendarItem.timezone_fields( ): # Timezone fields will (and must) be populated automatically from the timestamp continue if f.is_read_only: # These cannot be changed continue if not item.is_draft and f.is_read_only_after_send: # These cannot be changed when the item is no longer a draft continue if f.name == 'message_id' and f.is_read_only_after_send: # Cannot be updated, regardless of draft status continue if f.name == 'attachments': # Testing attachments is heavy. Leave this to specific tests update_kwargs[f.name] = [] continue if f.name == 'resources': # The test server doesn't have any resources update_kwargs[f.name] = [] continue if isinstance(f, AttachmentField): # Attachments are handled separately continue if f.name == 'start': start = get_random_date(start_date=insert_kwargs['end'].date()) update_kwargs[f.name], update_kwargs['end'] = \ get_random_datetime_range(start_date=start, end_date=start, tz=self.account.default_timezone) update_kwargs['recurrence'] = self.random_val( self.ITEM_CLASS.get_field_by_fieldname('recurrence')) update_kwargs['recurrence'].boundary.start = update_kwargs[ f.name].date() continue if f.name == 'end': continue if f.name == 'recurrence': continue if f.name == 'due_date': # start_date must be before due_date, and before complete_date which must be in the past update_kwargs['start_date'], update_kwargs[f.name] = \ get_random_datetime_range(end_date=now.date(), tz=self.account.default_timezone) continue if f.name == 'start_date': continue if f.name == 'status': # Update task to a completed state. complete_date must be a date in the past, and < than start_date update_kwargs[f.name] = Task.COMPLETED update_kwargs['percent_complete'] = Decimal(100) continue if f.name == 'percent_complete': continue if f.name == 'reminder_is_set': if self.ITEM_CLASS == Task: # Task type doesn't allow updating 'reminder_is_set' to True update_kwargs[f.name] = False else: update_kwargs[f.name] = not insert_kwargs[f.name] continue if isinstance(f, BooleanField): update_kwargs[f.name] = not insert_kwargs[f.name] continue if f.value_cls in (Mailbox, Attendee): if insert_kwargs[f.name] is None: update_kwargs[f.name] = self.random_val(f) else: update_kwargs[f.name] = None continue update_kwargs[f.name] = self.random_val(f) if self.ITEM_CLASS == CalendarItem: # EWS always sets due date to 'start' update_kwargs['reminder_due_by'] = update_kwargs['start'] if update_kwargs.get('is_all_day', False): # For is_all_day items, EWS will remove the time part of start and end values update_kwargs['start'] = update_kwargs['start'].date() update_kwargs['end'] = (update_kwargs['end'] + datetime.timedelta(days=1)).date() return update_kwargs