Skip to content

Instantly share code, notes, and snippets.

@adamwg
Created April 25, 2017 18:19
Show Gist options
  • Save adamwg/072f6dc46fc8c3a735d1afb947a40c37 to your computer and use it in GitHub Desktop.
Save adamwg/072f6dc46fc8c3a735d1afb947a40c37 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python2.7
import sys
import os.path
import argparse
import httplib2
import icalendar
from oauth2client import tools
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from copy import deepcopy
cal_id = "...@group.calendar.google.com"
calendar_url = "https://apidata.googleusercontent.com/caldav/v2/%s/events" % cal_id
def auth():
parser = argparse.ArgumentParser(parents=[tools.argparser])
flags = parser.parse_args()
flow = flow_from_clientsecrets(
'client_secrets.json', scope='https://www.googleapis.com/auth/calendar',
redirect_uri='urn:ietf:wg:oauth:2.0:oob')
storage = Storage('.creds')
return tools.run_flow(flow, storage, flags)
def get_creds():
storage = Storage('.creds')
creds = storage.get()
if creds is None or creds.invalid:
creds = auth()
return creds
def remove_extensions(component):
for k in component.keys():
if k.startswith('X-'):
del component[k]
if k.startswith('ORGANIZER'):
del component[k]
if k.startswith('ATTENDEE'):
del component[k]
def get_events(icsfile):
fd = open(icsfile)
txt = fd.read()
cal = icalendar.Calendar.from_ical(txt)
template = icalendar.Calendar()
for item in cal.items():
template.add(*item)
remove_extensions(template)
for sc in cal.subcomponents:
if type(sc) != icalendar.Event:
remove_extensions(sc)
template.add_component(sc)
events = dict()
for sc in cal.subcomponents:
remove_extensions(sc)
try:
uid = sc.decoded('uid')
except KeyError:
continue
if uid not in events:
ev = deepcopy(template)
ev.add_component(sc)
events[uid] = ev
else:
events[uid].add_component(sc)
return events
def get_existing_events(http):
(resp, content) = http.request(calendar_url, "GET")
cal = icalendar.Calendar.from_ical(content)
remove_extensions(cal)
events = dict()
for sc in cal.subcomponents:
if type(sc) == icalendar.Event:
remove_extensions(sc)
try:
uid = sc.decoded('uid')
except KeyError:
continue
events[uid] = sc
return events
def get_sync(new_events, old_events):
to_delete = dict()
to_update = dict()
to_add = dict()
# Delete anything in old that's not in new.
for (id, event) in old_events.items():
if not id in new_events:
to_delete[id] = event
for (id, event) in new_events.items():
# Update anything that's in both.
if id in old_events:
to_update[id] = event
else:
to_add[id] = event
return (to_delete, to_update, to_add)
def delete_event(id, event, http):
url = "%s/%s.ics" % (calendar_url, id)
print "Deleting event %s" % id
print "Getting etag"
(resp, content) = http.request(url, "GET")
try:
etag = resp['etag']
except KeyError:
print "Error getting etag"
print resp
return (None, None)
(resp, content) = http.request(url, "DELETE", headers={"if-match": etag})
return (resp, content)
def add_event(id, event, http):
url = "%s/%s.ics" % (calendar_url, id)
print "Adding event %s" % id
(resp, content) = http.request(url, "PUT", body=event.to_ical(),
headers={"content-type": "text/calendar",
"if-none-match": "*"})
return (resp, content)
def update_event(id, event, http):
url = "%s/%s.ics" % (calendar_url, id)
print "Updating event %s" % id
print "Getting etag"
(resp, content) = http.request(url, "GET")
try:
etag = resp['etag']
except KeyError:
print "Error getting etag"
print resp
return None
print "Sending update with etag %s" % etag
(resp, content) = http.request(url, "PUT", body=event.to_ical(),
headers={"content-type": "text/calendar",
"if-match": etag})
return (resp, content)
def main():
if len(sys.argv) < 2:
print "Usage: %s [args] <icsfile>" % sys.argv[0]
return
icsfile = sys.argv[-1]
if not os.path.exists(icsfile):
print "Usage: %s [args] <icsfile>" % sys.argv[0]
return
del sys.argv[-1]
creds = get_creds()
http = creds.authorize(httplib2.Http())
old_events = get_existing_events(http)
new_events = get_events(icsfile)
(to_delete, to_update, to_add) = get_sync(new_events, old_events)
print "delete %d / update %d / add %d" % (len(to_delete), len(to_update),
len(to_add))
for (id, event) in to_delete.items():
(resp, content) = delete_event(id, event, http)
if not resp:
continue
if int(resp['status']) > 299:
print "Delete failed"
print resp
for (id, event) in to_update.items():
(resp, content) = update_event(id, event, http)
if int(resp['status']) > 299:
print "Update failed"
print resp
for (id, event) in to_add.items():
(resp, content) = add_event(id, event, http)
if int(resp['status']) > 299:
print "Add failed"
print resp
print content
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment