From 98949bd42541aa55d880a04a38e8d32a642e3b72 Mon Sep 17 00:00:00 2001 From: dlichtistw Date: Sat, 29 Sep 2018 21:44:57 +0200 Subject: [PATCH 1/6] Test description Test event description refined. --- test/test_data/recurring.ics | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_data/recurring.ics b/test/test_data/recurring.ics index 1ceb0ff..6dc0679 100644 --- a/test/test_data/recurring.ics +++ b/test/test_data/recurring.ics @@ -5,7 +5,7 @@ END:VTIMEZONE BEGIN:VEVENT DTSTART;TZID=Europe/Berlin:20181003T100000 DTEND;TZID=Europe/Berlin:20181003T120000 -DESCRIPTION:Event recurring on wednesday each week +DESCRIPTION:Event recurring on wednesday each week, except on 2018-10-29 SUMARRY:Recurring event RRULE:FREQ=WEEKLY;BYDAY=MO EXDATE;TZID=Europe/Berlin:20181029T100000 From b862633fd7df782a6160915b01e4cece22883403 Mon Sep 17 00:00:00 2001 From: dlichtistw Date: Sun, 30 Sep 2018 12:07:43 +0200 Subject: [PATCH 2/6] Add test Add unittest for handling events with duration property instead of explicit dtend property. --- test/test_data/duration.ics | 14 ++++++++++++++ test/test_data/recurring.ics | 2 +- test/test_icalevents.py | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/test_data/duration.ics diff --git a/test/test_data/duration.ics b/test/test_data/duration.ics new file mode 100644 index 0000000..5d0a8de --- /dev/null +++ b/test/test_data/duration.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART:20180110 +DURATION:P1D +DESCRIPTION:Event with duration (1 day), instead of explicit end. +SUMMARY:Duration Event +END:VEVENT +BEGIN:VEVENT +DTSTART:20180120T100000 +DURATION:PT1H +DESCRIPTION:Event with duration (1 hour), instead of explicit end. +SUMMARY:Duration Event +END:VEVENT +END:VCALENDAR diff --git a/test/test_data/recurring.ics b/test/test_data/recurring.ics index 6dc0679..77175d9 100644 --- a/test/test_data/recurring.ics +++ b/test/test_data/recurring.ics @@ -6,7 +6,7 @@ BEGIN:VEVENT DTSTART;TZID=Europe/Berlin:20181003T100000 DTEND;TZID=Europe/Berlin:20181003T120000 DESCRIPTION:Event recurring on wednesday each week, except on 2018-10-29 -SUMARRY:Recurring event +SUMARRY:Recurring Event RRULE:FREQ=WEEKLY;BYDAY=MO EXDATE;TZID=Europe/Berlin:20181029T100000 END:VEVENT diff --git a/test/test_icalevents.py b/test/test_icalevents.py index b7ad17b..33200f0 100644 --- a/test/test_icalevents.py +++ b/test/test_icalevents.py @@ -31,6 +31,21 @@ def test_events(self): self.assertEqual(len(evs), 2, "two events are found") + def test_events_duration(self): + ical = "test/test_data/duration.ics" + start = date(2018, 1, 1) + end = date(2018, 2, 1) + + evs = icalevents.events(file=ical, start=start, end=end) + + e1 = evs[0] + self.assertEqual(e1.start.day, 10, "explicit event start") + self.assertEqual(e1.end.day, 11, "implicit event end") + + e2 = evs[1] + self.assertEqual(e2.start.hour, 10, "explicit event start") + self.assertEqual(e2.end.hour, 11, "implicit event end") + def test_events_recurring(self): ical = "test/test_data/recurring.ics" start = date(2018, 10, 15) From 24e1bb0ef7a9502fa81b06d7ddb98f2b6d9ee049 Mon Sep 17 00:00:00 2001 From: dlichtistw Date: Sun, 30 Sep 2018 12:28:41 +0200 Subject: [PATCH 3/6] Fix: Support events without end According to RFC5545, an event may have no DTEND nor DURATION property. Set end = start in that case. --- icalevents/icalparser.py | 14 ++++++++------ test/test_data/duration.ics | 7 ++++++- test/test_icalevents.py | 4 ++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/icalevents/icalparser.py b/icalevents/icalparser.py index 66a6577..69beeba 100644 --- a/icalevents/icalparser.py +++ b/icalevents/icalparser.py @@ -97,8 +97,6 @@ def copy_to(self, new_start=None, uid=None): :param uid: UID of new event :return: new event """ - duration = self.end - self.start - if not new_start: new_start = self.start @@ -109,7 +107,11 @@ def copy_to(self, new_start=None, uid=None): ne.summary = self.summary ne.description = self.description ne.start = new_start - ne.end = (new_start + duration) + + if self.end: + duration = self.end - self.start + ne.end = (new_start + duration) + ne.all_day = (self.all_day and (new_start - self.start).seconds == 0) ne.uid = uid @@ -131,10 +133,10 @@ def create_event(component, tz=UTC): if component.get('dtend'): event.end = normalize(component.get('dtend').dt, tz=tz) - elif component.get('duration'): + elif component.get('duration'): # compute implicit end as start + duration event.end = event.start + component.get('duration').dt - else: - raise ValueError("Event has neither end, nor duration property.") + else: # compute implicit end as start + 0 + event.end = event.start event.summary = str(component.get('summary')) event.description = str(component.get('description')) diff --git a/test/test_data/duration.ics b/test/test_data/duration.ics index 5d0a8de..4d2bef3 100644 --- a/test/test_data/duration.ics +++ b/test/test_data/duration.ics @@ -6,9 +6,14 @@ DESCRIPTION:Event with duration (1 day), instead of explicit end. SUMMARY:Duration Event END:VEVENT BEGIN:VEVENT -DTSTART:20180120T100000 +DTSTART:20180115T100000 DURATION:PT1H DESCRIPTION:Event with duration (1 hour), instead of explicit end. SUMMARY:Duration Event END:VEVENT +BEGIN:VEVENT +DTSTART:20180120T120000 +DESCRIPTION:Event without explicit dtend, nor duration property. +SUMMARY:Short event +END:VEVENT END:VCALENDAR diff --git a/test/test_icalevents.py b/test/test_icalevents.py index 33200f0..500e1b3 100644 --- a/test/test_icalevents.py +++ b/test/test_icalevents.py @@ -45,6 +45,10 @@ def test_events_duration(self): e2 = evs[1] self.assertEqual(e2.start.hour, 10, "explicit event start") self.assertEqual(e2.end.hour, 11, "implicit event end") + + e3 = evs[2] + self.assertEqual(e3.start.hour, 12, "explicit event start") + self.assertEqual(e3.end.hour, 12, "implicit event end") def test_events_recurring(self): ical = "test/test_data/recurring.ics" From de855637f17783d3b87f251869b1c46d2d29f67b Mon Sep 17 00:00:00 2001 From: dlichtistw Date: Mon, 1 Oct 2018 22:02:44 +0200 Subject: [PATCH 4/6] Add test Add test data and unittest for multiple exdates. --- test/test_data/recurring.ics | 9 +++++++++ test/test_icalevents.py | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/test/test_data/recurring.ics b/test/test_data/recurring.ics index 77175d9..7c903fd 100644 --- a/test/test_data/recurring.ics +++ b/test/test_data/recurring.ics @@ -10,4 +10,13 @@ SUMARRY:Recurring Event RRULE:FREQ=WEEKLY;BYDAY=MO EXDATE;TZID=Europe/Berlin:20181029T100000 END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20180601T100000 +DTEND;TZID=Europe/Berlin:20180601T120000 +DESCRIPTION:Event recurring on friday each week, except on 2018-06-08/22 +SUMARRY:Recurring Event +RRULE:FREQ=WEEKLY;BYDAY=FR +EXDATE;TZID=Europe/Berlin:20180608T100000 +EXDATE;TZID=Europe/Berlin:20180622T100000 +END:VEVENT END:VCALENDAR diff --git a/test/test_icalevents.py b/test/test_icalevents.py index 500e1b3..2f94c98 100644 --- a/test/test_icalevents.py +++ b/test/test_icalevents.py @@ -66,6 +66,17 @@ def test_events_recurring(self): self.assertEqual(e2.start.tzinfo.utcoffset(e2.start), timedelta(seconds=3600), "check UTC offset without DST") self.assertEqual(e2.start.day, 5, "Check observance of exdate.") + + def test_events_exdates(self): + ical = "test/test_data/recurring.ics" + start = date(2018, 6, 1) + end = date(2018, 6, 30) + + evs = icalevents.events(file=ical, start=start, end=end) + + self.assertEqual(evs[0].start.day, 1, "check first recurrence.") + self.assertEqual(evs[1].start.day, 15, "check first exdate.") + self.assertEqual(evs[2].start.day, 29, "check second exdate.") def test_event_attributes(self): ical = "test/test_data/basic.ics" From 001885677cc84053a018d967c959a7de843da856 Mon Sep 17 00:00:00 2001 From: dlichtistw Date: Tue, 2 Oct 2018 21:58:56 +0200 Subject: [PATCH 5/6] New unittest Add test for Event.__str__ method. Extend tests for Event.copy_to and normalize. --- test/test_data/duration.ics | 8 ++++---- test/test_icalevents.py | 29 ++++++++++++++++++++++++++--- test/test_icalparser.py | 8 ++++++-- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/test/test_data/duration.ics b/test/test_data/duration.ics index 4d2bef3..b686406 100644 --- a/test/test_data/duration.ics +++ b/test/test_data/duration.ics @@ -1,14 +1,14 @@ BEGIN:VCALENDAR BEGIN:VEVENT DTSTART:20180110 -DURATION:P1D -DESCRIPTION:Event with duration (1 day), instead of explicit end. +DURATION:P3D +DESCRIPTION:Event with duration (3 days), instead of explicit end. SUMMARY:Duration Event END:VEVENT BEGIN:VEVENT DTSTART:20180115T100000 -DURATION:PT1H -DESCRIPTION:Event with duration (1 hour), instead of explicit end. +DURATION:PT3H +DESCRIPTION:Event with duration (3 hours), instead of explicit end. SUMMARY:Duration Event END:VEVENT BEGIN:VEVENT diff --git a/test/test_icalevents.py b/test/test_icalevents.py index 2f94c98..dac3e05 100644 --- a/test/test_icalevents.py +++ b/test/test_icalevents.py @@ -1,7 +1,10 @@ import unittest from icalevents import icalevents -from datetime import date, timedelta +from datetime import date, timedelta, datetime from time import sleep +from dateutil.relativedelta import relativedelta +from dateutil.tz import UTC +from re import search class ICalEventsTests(unittest.TestCase): @@ -40,11 +43,11 @@ def test_events_duration(self): e1 = evs[0] self.assertEqual(e1.start.day, 10, "explicit event start") - self.assertEqual(e1.end.day, 11, "implicit event end") + self.assertEqual(e1.end.day, 13, "implicit event end") e2 = evs[1] self.assertEqual(e2.start.hour, 10, "explicit event start") - self.assertEqual(e2.end.hour, 11, "implicit event end") + self.assertEqual(e2.end.hour, 13, "implicit event end") e3 = evs[2] self.assertEqual(e3.start.hour, 12, "explicit event start") @@ -141,3 +144,23 @@ def test_string_data(self): self.assertTrue(icalevents.all_done(key), "request is finished") self.assertEqual(len(icalevents.latest_events(key)), 2, "two events are found") + + def test_event_str(self): + ical = "test/test_data/duration.ics" + start = date(2018, 1, 1) + end = date(2018, 2, 1) + n = datetime.now(UTC) + m = relativedelta(hour=0, minute=0, second=0, microsecond=0) + + evs = icalevents.events(file=ical, start=start, end=end) + + e1 = evs[0] + self.assertIsNotNone(search(r"ended", str(e1.copy_to(n - relativedelta(days=5) + m)))) + self.assertIsNotNone(search(r"today", str(e1.copy_to(n - relativedelta(days=1) + m)))) + self.assertIsNotNone(search(r"days left", str(e1.copy_to(n + relativedelta(days=3) + m)))) + + e2 = evs[1] + self.assertIsNotNone(search(r"ended", str(e2.copy_to(n - relativedelta(hours=5))))) + self.assertIsNotNone(search(r"now", str(e2.copy_to(n - relativedelta(hours=1))))) + self.assertIsNotNone(search(r"hours left", str(e2.copy_to(n + relativedelta(hours=3))))) + self.assertIsNotNone(search(r"days left", str(e2.copy_to(n + relativedelta(days=3))))) diff --git a/test/test_icalparser.py b/test/test_icalparser.py index c95745a..6b6b1a4 100644 --- a/test/test_icalparser.py +++ b/test/test_icalparser.py @@ -39,6 +39,7 @@ def test_time_left(self): def test_event_copy_to(self): new_start = datetime(year=2017, month=2, day=5, hour=12, minute=5, tzinfo=UTC) eventC = self.eventA.copy_to(new_start) + new_uid = 1234567890 self.assertNotEqual(eventC.uid, self.eventA.uid, "new event has new UID") self.assertEqual(eventC.start, new_start, "new event has new start") @@ -47,8 +48,8 @@ def test_event_copy_to(self): self.assertEqual(eventC.summary, self.eventA.summary, "copy to: summary") self.assertEqual(eventC.description, self.eventA.description, "copy to: description") - eventD = eventC.copy_to() - self.assertNotEqual(eventD.uid, eventC.uid, "new event has new UID") + eventD = eventC.copy_to(uid=new_uid) + self.assertEqual(eventD.uid, new_uid, "new event has specified UID") self.assertEqual(eventD.start, eventC.start, "new event has same start") self.assertEqual(eventD.end, eventC.end, "new event has same end") self.assertEqual(eventD.all_day, eventC.all_day, "new event is no all day event") @@ -84,3 +85,6 @@ def test_normalize(self): self.assertEqual(3, norm.second, "second") self.assertEqual(0, norm.microsecond, "microsecond") self.assertEqual(UTC, norm.tzinfo, "timezone") + + with self.assertRaises(ValueError, msg="type check effective"): + icalevents.icalparser.normalize(None) From dff4e359f7b9aeb64bb376bc5ce57f64c40c6077 Mon Sep 17 00:00:00 2001 From: dlichtistw Date: Tue, 2 Oct 2018 22:09:57 +0200 Subject: [PATCH 6/6] unittest description Add test messages for Event.__str__ tests. --- test/test_icalevents.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test_icalevents.py b/test/test_icalevents.py index dac3e05..f9ee2e6 100644 --- a/test/test_icalevents.py +++ b/test/test_icalevents.py @@ -155,12 +155,12 @@ def test_event_str(self): evs = icalevents.events(file=ical, start=start, end=end) e1 = evs[0] - self.assertIsNotNone(search(r"ended", str(e1.copy_to(n - relativedelta(days=5) + m)))) - self.assertIsNotNone(search(r"today", str(e1.copy_to(n - relativedelta(days=1) + m)))) - self.assertIsNotNone(search(r"days left", str(e1.copy_to(n + relativedelta(days=3) + m)))) + self.assertIsNotNone(search(r"ended", str(e1.copy_to(n - relativedelta(days=5) + m))), "stringify past event") + self.assertIsNotNone(search(r"today", str(e1.copy_to(n - relativedelta(days=1) + m))), "stringify ongoing event") + self.assertIsNotNone(search(r"days left", str(e1.copy_to(n + relativedelta(days=3) + m))), "stringify future event") e2 = evs[1] - self.assertIsNotNone(search(r"ended", str(e2.copy_to(n - relativedelta(hours=5))))) - self.assertIsNotNone(search(r"now", str(e2.copy_to(n - relativedelta(hours=1))))) - self.assertIsNotNone(search(r"hours left", str(e2.copy_to(n + relativedelta(hours=3))))) - self.assertIsNotNone(search(r"days left", str(e2.copy_to(n + relativedelta(days=3))))) + self.assertIsNotNone(search(r"ended", str(e2.copy_to(n - relativedelta(hours=5)))), "stringify past event") + self.assertIsNotNone(search(r"now", str(e2.copy_to(n - relativedelta(hours=1)))), "stringify ongoing event") + self.assertIsNotNone(search(r"hours left", str(e2.copy_to(n + relativedelta(hours=3)))), "stringify future event") + self.assertIsNotNone(search(r"days left", str(e2.copy_to(n + relativedelta(days=3)))), "stringify future event")