New general-purpose plugin for managing multiple calendar + support for ICal format
This commit is contained in:
parent
cb0a9f5c63
commit
bc3e100217
5 changed files with 154 additions and 2 deletions
|
@ -22,7 +22,7 @@ $(document).ready(function() {
|
||||||
execute(
|
execute(
|
||||||
{
|
{
|
||||||
type: 'request',
|
type: 'request',
|
||||||
action: 'google.calendar.get_upcoming_events',
|
action: 'calendar.get_upcoming_events',
|
||||||
args: {
|
args: {
|
||||||
max_results: 9,
|
max_results: 9,
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ $(document).ready(function() {
|
||||||
|
|
||||||
onSuccess = function(response) {
|
onSuccess = function(response) {
|
||||||
var events = response.response.output;
|
var events = response.response.output;
|
||||||
|
$eventsListContainer.html('');
|
||||||
|
|
||||||
for (var i=0; i < events.length; i++) {
|
for (var i=0; i < events.length; i++) {
|
||||||
var event = events[i];
|
var event = events[i];
|
||||||
var start = new Date('dateTime' in event.start ? event.start.dateTime : event.start.date);
|
var start = new Date('dateTime' in event.start ? event.start.dateTime : event.start.date);
|
||||||
|
|
72
platypush/plugins/calendar/__init__.py
Normal file
72
platypush/plugins/calendar/__init__.py
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import dateutil.parser
|
||||||
|
import importlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
from platypush.plugins import Plugin
|
||||||
|
from platypush.message.response import Response
|
||||||
|
|
||||||
|
|
||||||
|
class CalendarInterface:
|
||||||
|
__metaclass__ = ABCMeta
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_upcoming_events(self, max_results=10):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class CalendarPlugin(Plugin, CalendarInterface):
|
||||||
|
"""
|
||||||
|
The CalendarPlugin will allow you to keep track of multiple calendars
|
||||||
|
(Google or iCal URLs) and get joined events from all of them
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, calendars=[], *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Example calendars format:
|
||||||
|
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "platypush.plugins.google.calendar.GoogleCalendarPlugin"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "platypush.plugins.calendar.ical.IcalCalendarPlugin",
|
||||||
|
"url": "https://www.facebook.com/ical/u.php?uid=USER_ID&key=FB_KEY"
|
||||||
|
},
|
||||||
|
|
||||||
|
...
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.calendars = []
|
||||||
|
|
||||||
|
for calendar in calendars:
|
||||||
|
if 'type' not in calendar:
|
||||||
|
logging.warning("Invalid calendar with no type specified: {}".format(calendar))
|
||||||
|
continue
|
||||||
|
|
||||||
|
cal_type = calendar.pop('type')
|
||||||
|
module_name = '.'.join(cal_type.split('.')[:-1])
|
||||||
|
class_name = cal_type.split('.')[-1]
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
self.calendars.append(getattr(module, class_name)(**calendar))
|
||||||
|
|
||||||
|
|
||||||
|
def get_upcoming_events(self, max_results=10):
|
||||||
|
events = []
|
||||||
|
|
||||||
|
for calendar in self.calendars:
|
||||||
|
events.extend(calendar.get_upcoming_events().output)
|
||||||
|
|
||||||
|
events = sorted(events, key=lambda event:
|
||||||
|
dateutil.parser.parse(event['start']['dateTime']))[:max_results]
|
||||||
|
|
||||||
|
return Response(output=events)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
76
platypush/plugins/calendar/ical.py
Normal file
76
platypush/plugins/calendar/ical.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import datetime
|
||||||
|
import dateutil.parser
|
||||||
|
import requests
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
from icalendar import Calendar, Event
|
||||||
|
|
||||||
|
from platypush.message.response import Response
|
||||||
|
from platypush.plugins import Plugin
|
||||||
|
from platypush.plugins.calendar import CalendarInterface
|
||||||
|
|
||||||
|
|
||||||
|
class IcalCalendarPlugin(Plugin, CalendarInterface):
|
||||||
|
def __init__(self, url, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
|
||||||
|
def _translate_event(self, 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['cn']
|
||||||
|
} if event.get('organizer') else None,
|
||||||
|
|
||||||
|
'created': event.get('created').dt.isoformat() if event.get('created') else None,
|
||||||
|
'updated': event.get('last-modified').dt.isoformat() if event.get('last-modified') else None,
|
||||||
|
'start': {
|
||||||
|
'dateTime': event.get('dtstart').dt.isoformat() if event.get('dtstart') else None,
|
||||||
|
'timeZone': 'UTC', # TODO Support multiple timezone IDs
|
||||||
|
},
|
||||||
|
|
||||||
|
'end': {
|
||||||
|
'dateTime': event.get('dtend').dt.isoformat() if event.get('dtend') else None,
|
||||||
|
'timeZone': 'UTC',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_upcoming_events(self, max_results=10, only_participating=True):
|
||||||
|
events = []
|
||||||
|
response = requests.get(self.url)
|
||||||
|
|
||||||
|
if response.ok:
|
||||||
|
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'] and event['responseStatus'] \
|
||||||
|
and dateutil.parser.parse(event['end']['dateTime']) >= \
|
||||||
|
datetime.datetime.now(pytz.timezone('UTC')) \
|
||||||
|
and (
|
||||||
|
(only_participating
|
||||||
|
and event['status'] == 'confirmed'
|
||||||
|
and event['responseStatus'] in ['accepted', 'tentative'])
|
||||||
|
or not only_participating):
|
||||||
|
events.append(event)
|
||||||
|
else:
|
||||||
|
logging.error("HTTP error while getting {}: {}".format(self.url, response))
|
||||||
|
|
||||||
|
return Response(output=events)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -7,9 +7,10 @@ from apiclient import discovery
|
||||||
|
|
||||||
from platypush.message.response import Response
|
from platypush.message.response import Response
|
||||||
from platypush.plugins.google import GooglePlugin
|
from platypush.plugins.google import GooglePlugin
|
||||||
|
from platypush.plugins.calendar import CalendarInterface
|
||||||
|
|
||||||
|
|
||||||
class GoogleCalendarPlugin(GooglePlugin):
|
class GoogleCalendarPlugin(GooglePlugin, CalendarInterface):
|
||||||
scopes = ['https://www.googleapis.com/auth/calendar.readonly']
|
scopes = ['https://www.googleapis.com/auth/calendar.readonly']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -85,6 +85,7 @@ setup(
|
||||||
'Support for GPIO pins access': ['RPi.GPIO'],
|
'Support for GPIO pins access': ['RPi.GPIO'],
|
||||||
'Support for MCP3008 analog-to-digital converter plugin': ['adafruit-mcp3008'],
|
'Support for MCP3008 analog-to-digital converter plugin': ['adafruit-mcp3008'],
|
||||||
'Support for smart cards detection': ['pyscard'],
|
'Support for smart cards detection': ['pyscard'],
|
||||||
|
'Support for ICal calendars': ['icalendar', 'python-dateutil'],
|
||||||
# 'Support for Leap Motion backend': ['git+ssh://git@github.com:BlackLight/leap-sdk-python3.git', 'redis'],
|
# 'Support for Leap Motion backend': ['git+ssh://git@github.com:BlackLight/leap-sdk-python3.git', 'redis'],
|
||||||
# 'Support for Flic buttons': ['git+ssh://git@github.com/50ButtonsEach/fliclib-linux-hci']
|
# 'Support for Flic buttons': ['git+ssh://git@github.com/50ButtonsEach/fliclib-linux-hci']
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue