source: weekplanplugin/trunk/weekplan/ical_provider.py

Last change on this file was 18497, checked in by lucid, 14 months ago

WeekPlanPlugin: ical recurring events support
(fix #14122)

Fix AttributeError: 'datetime.date' object has no attribute 'date' (fix #14149)

Fix extras_require (see #14110)

File size: 3.6 KB
Line 
1import urllib
2import datetime
3
4from icalendar import Calendar
5import recurring_ical_events
6
7from trac.config import ConfigSection
8from trac.core import Component, implements
9from trac.util.datefmt import format_time, user_time
10from weekplan.core import IWeekPlanEventProvider
11from weekplan.model import WeekPlanEvent
12
13
14class ICalWeekPlanEventProvider(Component):
15    """Provides events from ical feeds like Google Calendar."""
16
17    implements(IWeekPlanEventProvider)
18
19    ical_section = ConfigSection('week-plan-ical-feeds',
20        """Every option in the `[week-plan-ical-feeds]` section defines one ical
21        feed. The option name defines the plan name. The option value defines
22        the URL.
23
24        '''Example:'''
25        {{{
26        [week-plan-ical-feeds]
27        trac = https://trac.edgewall.org/roadmap?format=ics
28        }}}
29        """)
30
31    def get_plan_prefixes(self):
32        return ['ical:']
33
34    def get_events(self, req, plan_ids, range):
35        def get_ical_entries(ical_url):
36            f = urllib.urlopen(ical_url)
37            content = f.read()
38            calendar = Calendar.from_ical(content)
39            entries = calendar.walk()
40            if range is not None:
41                r0, r1 = range
42                recurring_events = recurring_ical_events.of(calendar).between(r0, r1)
43                entries.extend(recurring_events)
44            return entries
45
46        def is_relevant(entry):
47            if entry.name != "VEVENT":
48                return False
49            start = entry.get('dtstart')
50            end = entry.get('dtend')
51            if start is None:
52                return False
53            if end is None:
54                end = start
55            if range is not None:
56                r0, r1 = range
57                if not isinstance(start.dt, datetime.datetime):
58                    r1 = r1.date()
59                if not isinstance(end.dt, datetime.datetime):
60                    r0 = r0.date()
61                if end.dt < r0 or r1 < start.dt:
62                    return False
63            return True
64
65        def ical_entry_to_week_plan_event(entry, plan):
66            entry_id = entry.get('UID')
67            start = entry.get('dtstart')
68            end = entry.get('dtend')
69            summary = entry.get('summary')
70            description = entry.get('description')
71            if end is None:
72                end = start
73            title = str(summary)
74            if description:
75                title += ' ' + str(description)
76            if isinstance(start.dt, datetime.datetime):
77                title = user_time(req, format_time, start.dt) + ' ' + title
78            start = start.dt
79            end = end.dt
80            if isinstance(start, datetime.date):
81                start = datetime.datetime.combine(start, datetime.time(12, 0))
82            if isinstance(end, datetime.date):
83                end = datetime.datetime.combine(end, datetime.time(12, 0))
84            return WeekPlanEvent(str(entry_id), plan, title, start, end)
85
86        events = []
87        for plan_suffix, ical_url in self.ical_section.options():
88            plan_id = 'ical:' + plan_suffix
89            if plan_ids and plan_id not in plan_ids:
90                continue
91            for entry in get_ical_entries(ical_url):
92                try:
93                    if is_relevant(entry):
94                        event = ical_entry_to_week_plan_event(entry, plan_id)
95                        events.append(event)
96                except BaseException as e:
97                    self.log.error("Error accessing week-plan '%s': %s", plan_id, e)
98        return events
Note: See TracBrowser for help on using the repository browser.