There are small typos in: - docs/configuration.rst - i3pystatus/calendar/google.py - i3pystatus/coin.py - i3pystatus/core/modules.py - i3pystatus/ping.py - i3pystatus/sabnzbd.py - i3pystatus/timer.py Fixes: - Should read `occurred` rather than `occured`. - Should read `subtracts` rather than `substracts`. - Should read `specify` rather than `specifiy`. - Should read `remaining` rather than `remainig`. - Should read `recommended` rather than `recomended`. - Should read `property` rather than `propertie`. - Should read `available` rather than `availible`.
136 lines
4.7 KiB
Python
136 lines
4.7 KiB
Python
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)
|
|
|
|
|
|
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()))
|
|
|
|
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
|
|
|
|
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()
|