Source code for platypush.plugins.calendar.ical

import datetime
from typing import Optional

import requests
from dateutil.tz import gettz

from platypush.plugins import Plugin, action
from platypush.plugins.calendar import CalendarInterface
from platypush.utils import utcnow


[docs] class CalendarIcalPlugin(Plugin, CalendarInterface): """ iCal calendars plugin. Interact with remote calendars in iCal format. """
[docs] def __init__(self, url, *args, **kwargs): """ :param url: iCal URL to parse :type url: str """ super().__init__(*args, **kwargs) self.url = url
@staticmethod def _convert_timestamp(event: dict, attribute: str) -> Optional[str]: t = event.get(attribute) if not t: return None if isinstance(t.dt, datetime.date) and not isinstance(t.dt, datetime.datetime): return datetime.datetime( t.dt.year, t.dt.month, t.dt.day, tzinfo=gettz() ).isoformat() return ( datetime.datetime.fromtimestamp(t.dt.timestamp()) .replace(tzinfo=t.dt.tzinfo or gettz()) .astimezone(datetime.timezone.utc) .isoformat() ) @classmethod def _translate_event(cls, event): return { 'id': str(event.get('uid')) if event.get('uid') else None, 'kind': 'calendar#event', 'summary': str(event.get('summary')) if event.get('summary') else None, 'description': ( str(event.get('description')) if event.get('description') else None ), 'status': str(event.get('status')).lower() if event.get('status') else None, 'responseStatus': ( str(event.get('partstat')).lower() if event.get('partstat') else None ), 'location': str(event.get('location')) if event.get('location') else None, 'htmlLink': str(event.get('url')) if event.get('url') else None, 'organizer': ( { 'email': str(event.get('organizer')).replace('MAILTO:', ''), 'displayName': event.get('organizer').params.get('cn'), } if event.get('organizer') else None ), 'created': cls._convert_timestamp(event, 'created'), 'updated': cls._convert_timestamp(event, 'last-modified'), 'start': { 'dateTime': cls._convert_timestamp(event, 'dtstart'), 'timeZone': 'UTC', }, 'end': { 'dateTime': cls._convert_timestamp(event, 'dtend'), 'timeZone': 'UTC', }, }
[docs] @action def get_upcoming_events(self, *_, only_participating=True, **__): """ Get the upcoming events. See :meth:`platypush.plugins.calendar.CalendarPlugin.get_upcoming_events`. """ from icalendar import Calendar events = [] response = requests.get(self.url, timeout=20) assert ( response.ok ), f"HTTP error while getting events from {self.url}: {response.text}" calendar = Calendar.from_ical(response.text) for event in calendar.walk(): if event.name != 'VEVENT': continue # Not an event event = self._translate_event(event) if ( event['status'] != 'cancelled' and event['end'].get('dateTime') and datetime.datetime.fromisoformat(event['end']['dateTime']) >= utcnow() and ( ( only_participating and event.get('responseStatus') in [None, 'accepted', 'tentative'] ) or not only_participating ) ): events.append(event) return events
# vim:sw=4:ts=4:et: