from oauth2client.client import AccessTokenRefreshError
import constants
import datetime
import file_writer
import gdata
import globals
import os
import renderer
import time
class gcal_renderer(renderer.debuggable_abstaining_renderer):
"""A renderer to fetch upcoming events from www.google.com/calendar"""
calendar_whitelist = frozenset([
'Alex\'s calendar',
'Family',
'Holidays in United States',
'Lynn Gasch',
'Lynn\'s Work',
'scott.gasch@gmail.com',
'Scott Gasch External - Misc',
'Birthdays', # <-- from g+ contacts
])
class comparable_event(object):
"""A helper class to sort events."""
def __init__(self, start_time, end_time, summary, calendar):
if start_time is None:
assert(end_time is None)
self.start_time = start_time
self.end_time = end_time
self.summary = summary
self.calendar = calendar
def __lt__(self, that):
if self.start_time is None and that.start_time is None:
return self.summary < that.summary
if self.start_time is None or that.start_time is None:
return self.start_time is None
return (self.start_time,
self.end_time,
self.summary,
self.calendar) < (that.start_time,
that.end_time,
that.summary,
that.calendar)
def __str__(self):
return '[%s] %s' % (self.timestamp(), self.friendly_name())
def friendly_name(self):
name = self.summary
name = name.replace("countdown:", "")
return "%s" % name
def timestamp(self):
if self.start_time is None:
return "None"
elif (self.start_time.hour == 0):
return datetime.datetime.strftime(self.start_time,
'%a %b %d %Y')
else:
return datetime.datetime.strftime(self.start_time,
'%a %b %d %Y %H:%M%p')
def __init__(self, name_to_timeout_dict, oauth):
super(gcal_renderer, self).__init__(name_to_timeout_dict, True)
self.oauth = oauth
self.client = self.oauth.calendar_service()
self.sortable_events = []
self.countdown_events = []
def debug_prefix(self):
return "gcal"
def periodic_render(self, key):
self.debug_print('called for "%s"' % key)
if (key == "Render Upcoming Events"):
return self.render_upcoming_events()
elif (key == "Look For Triggered Events"):
return self.look_for_triggered_events()
else:
raise error('Unexpected operation')
def render_upcoming_events(self):
page_token = None
def format_datetime(x):
return datetime.datetime.strftime(x, '%Y-%m-%dT%H:%M:%SZ')
time_min = datetime.datetime.now()
time_max = time_min + datetime.timedelta(95)
time_min, time_max = list(map(format_datetime, (time_min, time_max)))
self.debug_print("time_min is %s" % time_min)
self.debug_print("time_max is %s" % time_max)
# Writes 2 files:
# + "upcoming events",
# + a countdown timer for a subser of events,
f = file_writer.file_writer('gcal_3_none.html')
f.write('
Upcoming Calendar Events:
\n')
f.write('\n')
g = file_writer.file_writer('countdown_3_7200.html')
g.write('Countdowns:
\n')
try:
self.sortable_events = []
self.countdown_events = []
while True:
calendar_list = self.client.calendarList().list(
pageToken=page_token).execute()
for calendar in calendar_list['items']:
if (calendar['summary'] in gcal_renderer.calendar_whitelist):
events = self.client.events().list(
calendarId=calendar['id'],
singleEvents=True,
timeMin=time_min,
timeMax=time_max,
maxResults=50).execute()
def parse_date(x):
y = x.get('date')
if y:
y = datetime.datetime.strptime(y, '%Y-%m-%d')
else:
y = x.get('dateTime')
if y:
y = datetime.datetime.strptime(y[:-6],
'%Y-%m-%dT%H:%M:%S')
else:
y = None
return y
for event in events['items']:
try:
summary = event['summary']
self.debug_print("event '%s' (%s to %s)" % (
summary, event['start'], event['end']))
start = parse_date(event['start'])
end = parse_date(event['end'])
self.sortable_events.append(
gcal_renderer.comparable_event(start,
end,
summary,
calendar['summary']))
if ('countdown' in summary or
'Holidays' in calendar['summary'] or
'Countdown' in summary):
self.debug_print("event is countdown worthy")
self.countdown_events.append(
gcal_renderer.comparable_event(start,
end,
summary,
calendar['summary']))
except Exception as e:
print("gcal unknown exception, skipping event.");
else:
self.debug_print("Skipping calendar '%s'" % calendar['summary'])
page_token = calendar_list.get('nextPageToken')
if not page_token: break
self.sortable_events.sort()
upcoming_sortable_events = self.sortable_events[:12]
for event in upcoming_sortable_events:
self.debug_print("sorted event: %s" % event.friendly_name())
f.write("""
%s
|
%s
|
\n""" % (event.timestamp(), event.friendly_name()))
f.write('
\n')
f.close()
self.countdown_events.sort()
upcoming_countdown_events = self.countdown_events[:12]
now = datetime.datetime.now()
count = 0
timestamps = { }
for event in upcoming_countdown_events:
eventstamp = event.start_time
delta = eventstamp - now
name = event.friendly_name()
x = int(delta.total_seconds())
if x > 0:
identifier = "id%d" % count
days = divmod(x, constants.seconds_per_day)
hours = divmod(days[1], constants.seconds_per_hour)
minutes = divmod(hours[1], constants.seconds_per_minute)
g.write('%d days, %02d:%02d until %s\n' % (identifier, days[0], hours[0], minutes[0], name))
timestamps[identifier] = time.mktime(eventstamp.timetuple())
count += 1
self.debug_print("countdown to %s is %dd %dh %dm" % (
name, days[0], hours[0], minutes[0]))
g.write('')
g.write('""");
g.close()
return True
except (gdata.service.RequestError, AccessTokenRefreshError):
print("********* TRYING TO REFRESH GCAL CLIENT *********")
self.oauth.refresh_token()
self.client = self.oauth.calendar_service()
return False
except:
raise
def look_for_triggered_events(self):
f = file_writer.file_writer(constants.gcal_imminent_pagename)
f.write('Imminent Upcoming Calendar Events:
\n
\n')
f.write('\n')
now = datetime.datetime.now()
count = 0
for event in self.sortable_events:
eventstamp = event.start_time
delta = eventstamp - now
x = int(delta.total_seconds())
if x > 0 and x <= constants.seconds_per_minute * 3:
days = divmod(x, constants.seconds_per_day)
hours = divmod(days[1], constants.seconds_per_hour)
minutes = divmod(hours[1], constants.seconds_per_minute)
eventstamp = event.start_time
name = event.friendly_name()
calendar = event.calendar
f.write(" %s (%s) upcoming in %d minutes.\n" % (name, calendar, minutes[0]))
count += 1
f.write("
")
f.close()
if count > 0:
globals.put("gcal_triggered", True)
else:
globals.put("gcal_triggered", False)
return True