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")
now = datetime.datetime.now()
time_min = now - datetime.timedelta(1)
time_max = now + 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_86400.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