Skip to content

Instantly share code, notes, and snippets.

@ClarkGoble
Last active December 20, 2015 17:29
Show Gist options
  • Save ClarkGoble/6169144 to your computer and use it in GitHub Desktop.
Save ClarkGoble/6169144 to your computer and use it in GitHub Desktop.
Python script for displaying a range of calendar events from the Calendar app in a nice formatted form
#!/usr/bin/env python
# -*- coding: utf-8 -*-
## comingevents.py
###################################################
# Lists in a nice format forthcoming events
from appscript import *
import osax
from AppKit import *
from Foundation import *
import datetime, time
import sys
import optparse
import parsedatetime.parsedatetime as pdt
# path for where to put the html file
html_path = "/Users/clarkgoble/Documents/comingevents.html"
def sethtmlclipboard(html):
"""puts the html on the clipboard as styled text"""
pb = NSPasteboard.generalPasteboard()
a = NSArray.arrayWithObject_(NSHTMLPboardType)
pb.declareTypes_owner_(a, None)
pb.setString_forType_( html, NSHTMLPboardType)
def datetimeFromString( s ):
"""parse date and time from natural string"""
# See http://stackoverflow.com/questions/1810432/handling-the-different-results-from-parsedatetime
p = pdt.Calendar()
result, what = p.parse( s )
dt = 0
if what in (1,2,3):
# result is struct_time
dt = datetime.datetime( *result[:6] )
if what == 0:
# Failed to parse
return ""
return dt
def make_range(numberofdays, starttime=None):
"""Makes a range from now to a specified number of days in the future"""
# calculate our date range
if starttime is None:
starttime = datetime.datetime.now()
endtime = starttime + datetime.timedelta(days = numberofdays)
return [starttime, endtime]
def get_range():
"""Brings up a dialog box to specify a date range"""
sa = osax.OSAX()
sa.activate()
# set up some defaults
startdate, enddate = make_range(15)
startstring = startdate.strftime( "%b %d %Y")
endstring = enddate.strftime( "%b %d %Y")
range = sa.display_dialog("Date range to display", with_title="Date Range", default_answer= startstring + " - " + endstring )
startstring, endstring = range[k.text_returned].split("-")
startstring = startstring.strip()
endstring = endstring.strip()
print startstring, endstring
startdate = datetimeFromString( startstring )
enddate = datetimeFromString( endstring )
# startdate = datetime.datetime.strptime( startstring, "%b %d %Y" )
# enddate = datetime.datetime.strptime( endstring, "%b %d %Y" )
return [startdate, enddate]
def sort_calinfo( events ):
"""sorts the array of calendar events by startdate"""
return sorted(events, key = lambda r: r['startdate'])
def print_calinfo( events ):
"""prints in plain text the events"""
for e in events:
print "{} Start: {} End: {} ".format( e['calendar'], e['startdate'], e['enddate'])
print e['summary']
print
return
def html_calinfo( events, startdate, enddate ):
"""returns the list of events as formatted html"""
html =u"""<html><body>
<style type="text/css">
body {{ background: #F2E2CD; font-family:Helvetica; font-size: 10pt; }}
#caltable {{ width: 480px; text-align: left; border-collapse: collapse; font-size: 10pt; }}
#caltable th {{ background: #b9c9fe; padding: 8px; color: #039; font-style:bold; font-size: 12pt; text-align: center; height: 28pt; vertical-align:top}}
#caltable td {{ padding: 12px; padding-bottom: 4px; background: #e8edff; border-top: 1px solid #fff; color: #669; }}
#caltable #note {{ padding:12px; padding-bottom: 4px; background: #e8edff; border-top: none; color: #669; font-size: 8pt; }}
#caltable #small {{ padding: 12px; background: #e8edff; border-top: none; color: #669; font-size: 8pt; }}
</style>
<div id="cal">
<table id="caltable">
<thead><tr>
<th colspan=4>Calendar {} — {}</th></thead>
<tbody>\n""".format( startdate.strftime( "%I:%M %p &nbsp; %b %d %Y "), enddate.strftime( "%b %d %Y") )
# — = \u2014
for e in events:
html = html + u"""<tr><td colspan=4>{}<br/></td></tr>\n""".format( e['summary'] )
if e['note'] != k.missing_value and e['note'] != None:
html = html + u"""<tr><td id="note"></td><td id="note" colspan=3>{}</td></tr>\n""".format( e['note'] )
if e['allday'] == True:
html = html + u"""<tr><td id="small"></td><td id="small">Calendar: {}</td><td id="small" colspan=2>{}</td></tr>\n""".format(
e['calendar'], e['startdate'].strftime( "%b %d %Y &nbsp; &nbsp; (All Day)") )
else:
html = html + u"""<tr><td id="small"></td><td id="small">Calendar: {}</td><td id="small" colspan=2>{} — {}</td></tr>\n""".format(
e['calendar'], e['startdate'].strftime( " %b %d %Y &nbsp; &nbsp; %I:%M %p"), e['enddate'].strftime( "%I:%M %p") )
html = html + u"""\n</table></div></body></html>"""
return html
def get_calinfo(startdate, enddate):
"""Gets the list of events for the next numberofdays """
# all the calendars
Cal = app(u'Calendar').calendars.get()
print startdate, enddate
# empty array of events we'll fill
events = []
for c in Cal:
name = c.name()
eventlist = c.events[(its.start_date > startdate).AND(its.start_date < enddate)]()
for e in eventlist:
eventstart = e.start_date()
if eventstart == None:
continue
eventend = e.end_date()
summary = e.summary()
note = e.description()
allday = e.allday_event()
#description = e.description
d = {'calendar':name, 'startdate':eventstart, 'enddate':eventend, 'summary':summary, 'note':note, 'allday':allday}
events.append( d )
return events
def updatehtml(html):
"""Saves the html to a file"""
f = open(html_path, "w")
f.write(html.encode('utf16'))
f.close()
def openhtml(html):
"""Opens html with Safari"""
Safari = app(u'/Applications/Safari.app')
tab = Safari.windows[1].make(new=k.tab, with_properties={k.URL: u'file://'+html_path })
Safari.windows[1].current_tab.set( tab )
def interactive():
"""Runs the script interactively bringing up a dialog to prompt for a date range"""
startdate, enddate = get_range()
print startdate, enddate
events = get_calinfo(startdate, enddate)
events = sort_calinfo( events )
html = html_calinfo( events, startdate, enddate )
print html
sethtmlclipboard( html )
updatehtml(html)
openhtml( html )
def automated(startdate, enddate):
"""Runs the script in an automated fashion with no interaction"""
events = get_calinfo(startdate, enddate)
events = sort_calinfo( events )
html = html_calinfo( events, startdate, enddate )
print html
sethtmlclipboard( html )
updatehtml( html )
openhtml( html )
def main():
"""Handle command line options and run appropriately"""
usage = "useage: %prog [options]"
option_parser = optparse.OptionParser()
option_parser.add_option('-i','--interactive', action='store_true', dest='interactive', default=False,
help="run interactively with a dialog box")
option_parser.add_option('-s',"--start", action='store', type='string', dest='starttime',
help="start date for display")
option_parser.add_option('-e',"--end", action='store', type='string',dest='endtime',
help="end date for display")
option_parser.add_option('-d',"--days", action='store', type='int',dest='days',
help="days to display")
options, args = option_parser.parse_args()
if options.interactive is True:
interactive()
return
if options.days is not None:
if options.starttime is None:
startdate = datetime.datetime.now()
else:
# startdate = datetime.datetime.strptime( options.starttime, "%b %d %Y" )
startdate = datetimeFromString( option.starttime )
startdate, enddate = make_range( int(options.days), startdate)
automated( startdate, enddate )
return
if options.starttime is None and options.endtime is not None:
startdate = datetime.datetime.now()
# enddate = datetime.datetime.strptime( options.endtime, "%b %d %Y" )
enddate = datetimeFromString( options.endtime )
automated( startdate, enddate )
return
if options.starttime is None and options.endtime is None:
startdate, enddate = make_range(10)
automated( startdate, enddate )
return
# startdate = datetime.datetime.strptime( options.starttime, "%b %d %Y" )
# enddate = datetime.datetime.strptime( options.endtime, "%b %d %Y" )
startdate = datetimeFromString( options.starttime )
enddate = datetimeFromString( options.endtime )
automated( startdate, enddate )
return
if __name__ == '__main__':
main()
# change to 0 for success, 1 for (partial) failure
sys.exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment