From 67142bc6fee702c9d66685787a19d26c8d70a8dd Mon Sep 17 00:00:00 2001 From: facetoe Date: Wed, 20 Jan 2016 22:58:11 +0800 Subject: [PATCH 1/4] Add GoogleCalendar module --- i3pystatus/google_calendar.py | 120 ++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 i3pystatus/google_calendar.py diff --git a/i3pystatus/google_calendar.py b/i3pystatus/google_calendar.py new file mode 100644 index 0000000..f995403 --- /dev/null +++ b/i3pystatus/google_calendar.py @@ -0,0 +1,120 @@ +import datetime + +import httplib2 +import oauth2client +import pytz +from apiclient import discovery +from dateutil import parser + +from i3pystatus import IntervalModule +from i3pystatus.core.color import ColorRangeModule +from i3pystatus.core.util import internet, require + + +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 + * `{htmlLink}` — link to the calendar event + + + """ + settings = ( + ('format', 'format string'), + ("credential_path", "Path to credentials"), + ("skip_recurring", "Skip recurring events."), + ("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',) + + format = "{summary} ({remaining_time})" + urgent_seconds = 300 + interval = 30 + color = '#FFFFFF' + skip_recurring = True + credential_path = None + + service = None + credentials = None + + 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() + + display_event = self.get_next_event() + if display_event: + start_time = parser.parse(display_event['start']['dateTime']) + now = datetime.datetime.now(tz=pytz.UTC) + + alert_time = now + datetime.timedelta(seconds=self.urgent_seconds) + 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(**display_event), + 'color': color, + 'urgent': urgent + } + else: + 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(): + start_time = parser.parse(event['start']['dateTime']) + now = datetime.datetime.now(tz=pytz.UTC) + if 'recurringEventId' in event and self.skip_recurring: + continue + elif start_time < now: + continue + return event + + def get_events(self): + 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() + return events_result.get('items', []) + + def get_timerange(self): + now = datetime.datetime.utcnow() + later = now + datetime.timedelta(days=1) + 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 From c21a98cde0bb33ffc4a7ea7a1bd04a243c486740 Mon Sep 17 00:00:00 2001 From: facetoe Date: Wed, 10 Feb 2016 22:14:51 +0800 Subject: [PATCH 2/4] Add google-api-python-client to prevent build failing. Not sure if correct solution... --- dev-requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-requirements.txt b/dev-requirements.txt index ed38b0e..62e971c 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,3 +3,4 @@ sphinx>=1.1 colour>=0.0.5 mock>=1.0 pep8>=1.5.7 +google-api-python-client>=1.4.2 From 60f6d200a8698a2b3bd9b222e3d0d60ef0b3643f Mon Sep 17 00:00:00 2001 From: facetoe Date: Wed, 10 Feb 2016 22:19:11 +0800 Subject: [PATCH 3/4] Add python-dateutil to dev-requirements.txt --- dev-requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-requirements.txt b/dev-requirements.txt index 62e971c..d1484b2 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -4,3 +4,4 @@ colour>=0.0.5 mock>=1.0 pep8>=1.5.7 google-api-python-client>=1.4.2 +python-dateutil>=2.4.2 From 4abff4bc0a02596e2dd81de23aa1faa4b3ff631d Mon Sep 17 00:00:00 2001 From: facetoe Date: Sun, 14 Feb 2016 19:13:42 +0800 Subject: [PATCH 4/4] Place mock modules in correct location --- dev-requirements.txt | 2 -- docs/conf.py | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index d1484b2..ed38b0e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,5 +3,3 @@ sphinx>=1.1 colour>=0.0.5 mock>=1.0 pep8>=1.5.7 -google-api-python-client>=1.4.2 -python-dateutil>=2.4.2 diff --git a/docs/conf.py b/docs/conf.py index 7da9e13..8d46f11 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,6 +35,12 @@ MOCK_MODULES = [ "speedtest_cli", "pyzabbix", "vk", + "google-api-python-client", + "dateutil", + "httplib2", + "oauth2client", + "apiclient" + ] for mod_name in MOCK_MODULES: