From cd6878f8cf048756e847a46dc2ff020633d633a0 Mon Sep 17 00:00:00 2001 From: Ace Date: Sat, 14 Dec 2019 17:24:05 +0100 Subject: [PATCH 1/2] Added support for recurring events This is a more thorough approach on fetching and parsing recurring events. It has been tested on Google and Yahoo iCalendar, so it might not work on others. As further testing and feedpack is required to test it's reliability, the parsing of reucrring events is switched off by default. Setting 'use_recurring_events' to 'True' activates parsing of recurring events. --- modules/inkycal_icalendar.py | 41 +++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/modules/inkycal_icalendar.py b/modules/inkycal_icalendar.py index a7bff5b..f056fb3 100644 --- a/modules/inkycal_icalendar.py +++ b/modules/inkycal_icalendar.py @@ -10,13 +10,19 @@ from settings import ical_urls import arrow from ics import Calendar +use_recurring_events = False ## Attention: experimental feature! print_events = False style = 'DD MMM YY HH:mm' +if use_recurring_events == True: + from dateutil.rrule import rrulestr, rruleset + import re + def fetch_events(): """Set timelines for filtering upcoming events""" - now = arrow.now(tz=get_tz()) + timezone = get_tz() + now = arrow.now(tz=timezone) beginning_of_month = now.replace(days= - now.day +1) near_future = now.replace(days= 30) further_future = now.replace(days=40) @@ -29,6 +35,32 @@ def fetch_events(): if beginning_of_month <= events.end <= further_future or beginning_of_month <= events.begin <= near_future] + """Try to parse recurring events. This is clearly experimental! """ + if use_recurring_events == True: + for ical in calendars: + for events in ical.events: + event_str = str(events) + if re.search('RRULE:(.+?)\n', event_str): + if events.all_day and events.duration.days > 1: + events.end = events.end.replace(days=-2) + else: + events.begin = events.begin.to(timezone) + events.end = events.end.to(timezone) + rule = re.search('RRULE:(.+?)\n', event_str).group(0)[:-2] + if re.search('UNTIL=(.+?);', rule) and not re.search('UNTIL=(.+?)Z;', rule): + rule = re.sub('UNTIL=(.+?);', 'UNTIL='+re.search('UNTIL=(.+?);', rule).group(0)[6:-1]+'T000000Z;', rule) + dates = rrulestr(rule, dtstart= events.begin.datetime).between(after= now.datetime, before = further_future.datetime) + + if dates: + duration = events.duration + for date in dates: + cc = events.clone() + cc.end = arrow.get(date+duration) + cc.begin = arrow.get(date) + upcoming_events.append(cc) + #print("Added '{}' with new start at {}".format(cc.name, cc.begin.format('DD MMM YY'))) + + """Sort events according to their beginning date""" def sort_dates(event): return event.begin @@ -38,11 +70,10 @@ def fetch_events(): for events in upcoming_events: if events.all_day and events.duration.days > 1: events.end = events.end.replace(days=-2) - + if not events.all_day: - events.begin = events.begin.to(get_tz()) - events.end = events.end.to(get_tz()) - + events.begin = events.begin.to(timezone) + events.end = events.end.to(timezone) """ The list upcoming_events should not be modified. If you need the data from this one, copy the list or the contents to another one.""" From 7651feb7df1a3c7d4718fa7bdd8c624f18ef3bd5 Mon Sep 17 00:00:00 2001 From: Ace Date: Sat, 14 Dec 2019 19:19:44 +0100 Subject: [PATCH 2/2] Added Error handling for recurring events With the added Error handling, the stability of the recurring-events-parsing section has improved as well. This is handy when using non-supported iCalendars. --- modules/inkycal_icalendar.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/inkycal_icalendar.py b/modules/inkycal_icalendar.py index f056fb3..044df27 100644 --- a/modules/inkycal_icalendar.py +++ b/modules/inkycal_icalendar.py @@ -46,20 +46,24 @@ def fetch_events(): else: events.begin = events.begin.to(timezone) events.end = events.end.to(timezone) - rule = re.search('RRULE:(.+?)\n', event_str).group(0)[:-2] - if re.search('UNTIL=(.+?);', rule) and not re.search('UNTIL=(.+?)Z;', rule): - rule = re.sub('UNTIL=(.+?);', 'UNTIL='+re.search('UNTIL=(.+?);', rule).group(0)[6:-1]+'T000000Z;', rule) - dates = rrulestr(rule, dtstart= events.begin.datetime).between(after= now.datetime, before = further_future.datetime) + try: + rule = re.search('RRULE:(.+?)\n', event_str).group(0)[:-2] + if re.search('UNTIL=(.+?);', rule) and not re.search('UNTIL=(.+?)Z;', rule): + rule = re.sub('UNTIL=(.+?);', 'UNTIL='+re.search('UNTIL=(.+?);', rule).group(0)[6:-1]+'T000000Z;', rule) + dates = rrulestr(rule, dtstart= events.begin.datetime).between(after= now.datetime, before = further_future.datetime) - if dates: - duration = events.duration - for date in dates: - cc = events.clone() - cc.end = arrow.get(date+duration) - cc.begin = arrow.get(date) - upcoming_events.append(cc) - #print("Added '{}' with new start at {}".format(cc.name, cc.begin.format('DD MMM YY'))) + if dates: + duration = events.duration + for date in dates: + cc = events.clone() + cc.end = arrow.get(date+duration) + cc.begin = arrow.get(date) + upcoming_events.append(cc) + #print("Added '{}' with new start at {}".format(cc.name, cc.begin.format('DD MMM YY'))) + except Exception as e: + print('Something went wrong while parsing recurring events') + pass """Sort events according to their beginning date""" def sort_dates(event):