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
[docs] def get_timerange_formatted(self, now): """ Return two ISO8601 formatted date strings, one for timeMin, the other for timeMax (to be consumed by get_events) """ later = now + datetime.timedelta(days=self.days) return now.isoformat(), later.isoformat()