Source code for i3pystatus.google_calendar

import datetime

import httplib2
import oauth2client
import pytz
from apiclient import discovery
from dateutil import parser
from googleapiclient.errors import HttpError
from i3pystatus import IntervalModule, logger
from i3pystatus.core.color import ColorRangeModule
from i3pystatus.core.util import internet, require, user_open


[docs]class GoogleCalendar(IntervalModule, ColorRangeModule): """ Simple module for displaying next Google Calendar event. Requires the Google Calendar API package - https://developers.google.com/google-apps/calendar/quickstart/python. Additionally requires the `colour`, `httplib2`, `oauth2client`, `pytz`, `apiclient` and `dateutil` modules. All top level keys returned by the Google Calendar API can be used as formatters. Some examples include: .. rubric:: Available formatters * `{kind}` — type of event * `{status}` — eg, confirmed * `{summary}` — essentially the title * `{remaining_time}` - how long remaining until the event * `{start_time}` - when this event starts * `{htmlLink}` — link to the calendar event """ settings = ( ('format', 'format string'), ("credential_path", "Path to credentials"), ("skip_recurring", "Skip recurring events."), ("days", "Only show events between now and this many days in the future"), ("urgent_seconds", "Add urgent hint when this many seconds until event startTime"), ("start_color", "Hex or English name for start of color range, eg '#00FF00' or 'green'"), ("end_color", "Hex or English name for end of color range, eg '#FF0000' or 'red'"), ) required = ('credential_path',) interval = 30 format = "{summary} ({remaining_time})" credential_path = None skip_recurring = True days = 1 urgent_seconds = 300 color = None service = None credentials = None display_event = None def on_click(self, button, **kwargs): self.open_calendar() def init(self): self.colors = self.get_hex_color_range(self.end_color, self.start_color, self.urgent_seconds * 2) @require(internet) def run(self): if not self.service: self.connect_service() try: self.display_event = self.get_next_event() except ConnectionResetError as e: logger.warn(e) if self.display_event: start_time = self.display_event['start_time'] now = datetime.datetime.now(tz=pytz.UTC) alert_time = now + datetime.timedelta(seconds=self.urgent_seconds) self.display_event['remaining_time'] = str((start_time - now)).partition('.')[0] urgent = alert_time > start_time color = self.get_color(now, start_time) self.output = { 'full_text': self.format.format(**self.display_event), 'color': color, 'urgent': urgent } else: self.display_event = None self.output = { 'full_text': "", } def connect_service(self): self.credentials = oauth2client.file.Storage(self.credential_path).get() self.service = discovery.build('calendar', 'v3', http=self.credentials.authorize(httplib2.Http())) def get_next_event(self): for event in self.get_events(): # If we don't have a dateTime just make do with a date. if 'dateTime' not in event['start']: event['start_time'] = pytz.utc.localize(parser.parse(event['start']['date'])) else: event['start_time'] = parser.parse(event['start']['dateTime']) now = datetime.datetime.now(tz=pytz.UTC) if 'recurringEventId' in event and self.skip_recurring: continue elif event['start_time'] < now: continue # It is possible for there to be no title... if 'summary' not in event: event['summary'] = '(no title)' return event def get_events(self): events = [] try: now, later = self.get_timerange() events_result = self.service.events().list( calendarId='primary', timeMin=now, timeMax=later, maxResults=10, singleEvents=True, orderBy='startTime', timeZone='utc' ).execute() events = events_result.get('items', []) except HttpError as e: if e.resp.status in (500, 503): logger.warn("GoogleCalendar received %s while retrieving events" % e.resp.status) else: raise return events def get_timerange(self): now = datetime.datetime.utcnow() later = now + datetime.timedelta(days=self.days) now = now.isoformat() + 'Z' later = later.isoformat() + 'Z' return now, later def get_color(self, now, start_time): seconds_to_event = (start_time - now).seconds v = self.percentage(seconds_to_event, self.urgent_seconds) color = self.get_gradient(v, self.colors) return color def open_calendar(self): if self.display_event: calendar_url = self.display_event.get('htmlLink', None) if calendar_url: user_open(calendar_url)