Source code for i3pystatus.calendar.google
import datetime
from datetime import timezone
import httplib2
from oauth2client import file as file_, client, tools
import pytz
from googleapiclient import discovery
from dateutil import parser
from googleapiclient.errors import HttpError
from i3pystatus.calendar import CalendarBackend, CalendarEvent, formatter
from i3pystatus.core.util import user_open, require, internet
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
class GoogleCalendarEvent(CalendarEvent):
def __init__(self, google_event):
self.id = google_event['id']
self.title = google_event['summary']
self.start = self._parse_date(google_event['start'])
self.end = self._parse_date(google_event['end'])
self.recurring = 'recurringEventId' in google_event
self._link = google_event['htmlLink']
self._status = google_event['status']
self._kind = google_event['kind']
@formatter
def htmlLink(self):
return self._link
@formatter
def status(self):
return self._status
@formatter
def kind(self):
return self._kind
def _parse_date(self, date_section):
if 'dateTime' not in date_section:
result = parser.parse(date_section['date'])
else:
result = parser.parse(date_section['dateTime'])
return result.replace(tzinfo=timezone.utc).astimezone(tz=None)
[docs]class Google(CalendarBackend):
"""
Calendar backend for interacting with Google Calendar.
Requires the Google Calendar API package - https://developers.google.com/google-apps/calendar/quickstart/python.
Additionally requires the `colour`, `httplib2`, `oauth2client`, `pytz`, `google-api-python-client` and `dateutil` modules.
The first time this module is ran, you will need to specify the location of `credentials.json` (as credentials_json)
acquired from: https://developers.google.com/google-apps/calendar/quickstart/python
this will open a browser window for auth, and save a token to `credential_path`. you will need to reload i3poystatus
afterwards
If you already have a token `credentials_json` is not required (though highly recommended incase your token gets broken)
.. rubric:: Available formatters
* `{kind}` — type of event
* `{status}` — eg, confirmed
* `{htmlLink}` — link to the calendar event
"""
settings = (
('credential_path', 'Path to save credentials to (auto generated the first time this module is ran)'),
('credentials_json', 'path to credentials.json (generated by google)'),
('days', 'Only show events between now and this many days in the future'),
)
required = ('credential_path',)
credentials_json = None
days = 7
def init(self):
self.service = None
self.events = []
@require(internet)
def update(self):
if self.service is None:
self.connect_service()
self.refresh_events()
def on_click(self, event):
user_open(event.htmlLink())
def connect_service(self):
self.logger.debug("Connecting Service..")
store = file_.Storage(self.credential_path)
self.credentials = store.get()
# if the module is being ran for the first time, open up the browser to authenticate
if not self.credentials or self.credentials.invalid:
flow = client.flow_from_clientsecrets(self.credentials_json, SCOPES)
self.credentials = tools.run_flow(flow, store)
self.service = discovery.build('calendar', 'v3', http=self.credentials.authorize(httplib2.Http()))
[docs] def refresh_events(self):
"""
Retrieve the next N events from Google.
"""
now = datetime.datetime.now(tz=pytz.UTC)
try:
now, later = self.get_timerange_formatted(now)
events_result = self.service.events().list(
calendarId='primary',
timeMin=now,
timeMax=later,
maxResults=10,
singleEvents=True,
orderBy='startTime',
timeZone='utc'
).execute()
self.events.clear()
for event in events_result.get('items', []):
self.events.append(GoogleCalendarEvent(event))
except HttpError as e:
if e.resp.status in (500, 503):
self.logger.warn("GoogleCalendar received %s while retrieving events" % e.resp.status)
else:
raise