Skip to content

Instantly share code, notes, and snippets.

@xqms
Created August 25, 2010 19:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xqms/550096 to your computer and use it in GitHub Desktop.
Save xqms/550096 to your computer and use it in GitHub Desktop.
Google calendar plugin for opensync
"""
Google calendar plugin for opensync.
Installation:
- Install the opensync python plugin (debian: opensync-module-python)
- Copy this file to /usr/lib/opensync/python-plugins/ (may have to create the directory)
Quite ugly, but works for me.
If someone wants to work on it:
- opensync-style configuration (Google AuthSub session token)
- contacts?
- gdata setup in SyncClass
- msynctool --sync hangs at the end, not sure if it is my fault...
Author: Max Schwarz <Max@x-quadraht.de>
"""
# CONFIGURATION
# Google session token (see Google AuthSub documentation)
GOOGLE_TOKEN = '1/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
#########################################################################
import opensync
import atom
atom.MEMBER_STRING_ENCODING=unicode
import gdata
import gdata.calendar.service
import gdata.service
import gdata.calendar
import hashlib
import icalendar
from icalendar.cal import Component
from icalendar.prop import vText, vRecur
from datetime import datetime, timedelta
import iso8601
from xml.sax.saxutils import escape
from pprint import pprint
calendar_service = gdata.calendar.service.CalendarService()
calendar_service.ssl = True
calendar_service.SetAuthSubToken(GOOGLE_TOKEN)
class SyncClass:
def __init__(self, member):
self.member = member
self.hashtable = opensync.OSyncHashTable()
if not self.hashtable.load(self.member):
raise OpenSyncError('hashtable load failed',
opensync.ERROR_INITIALIZATION)
def connect(self, ctx):
ctx.report_success()
def disconnect(self, ctx):
ctx.report_success()
def finalize(self):
pass
def sync_done(self, ctx):
ctx.report_success()
def parse_dt(self, dt):
try:
return iso8601.parse_date(dt)
except:
return datetime.strptime(dt, '%Y-%m-%d').date()
def event_to_data(self, event):
def zero_if_none(arg):
if arg == None:
return 0
else:
return int(arg)
cal = icalendar.Calendar()
cal.add('prodid', '-//GCAL OPENSYNC IMPORTER//x-quadraht.de//')
cal.add('version', '2.0')
cevent = icalendar.Event()
cevent.add('summary', unicode(event.title.text))
cevent.add('categories', 'Meeting') # FIXME
cevent.add('location', '')
if event.recurrence:
component = Component.from_string('BEGIN:GCALRECUR\n' + event.recurrence.text + 'END:GCALRECUR\n')
for name in ('RRULE', 'DTSTART', 'DTEND'):
if name in component:
cevent.add(name, component[name], encode=0)
print "Setting", name, "=", component[name]
if len(event.when) == 1:
cevent.add('dtstart', self.parse_dt(event.when[0].start_time))
cevent.add('dtend', self.parse_dt(event.when[0].end_time))
for reminder in event.when[0].reminder:
if reminder.extension_attributes['method'] != 'alert':
continue
alarm = icalendar.Alarm(action='DISPLAY')
if reminder.absolute_time:
alarm.add('trigger', self.parse_dt(reminder.absolute_time))
else:
alarm.add('trigger', -timedelta(
days=zero_if_none(reminder.days),
hours=zero_if_none(reminder.hours),
minutes=zero_if_none(reminder.minutes),
))
cevent.add_component(alarm)
# Reminders for recurring events
reminders = event.FindExtensions('reminder')
for reminder in reminders:
if reminder.attributes['method'] != 'alert':
continue
alarm = icalendar.Alarm(action='DISPLAY')
if 'absolute_time' in reminder.attributes:
alarm.add('trigger', self.parse_dt(reminder.attributes['absolute_time']))
else:
alarm.add('trigger', -timedelta(
days=int(reminder.attributes.get('days', 0)),
hours=int(reminder.attributes.get('hours', 0)),
minutes=int(reminder.attributes.get('minutes', 0)),
))
cevent.add_component(alarm)
cal.add_component(cevent)
return cal.as_string()
def get_changeinfo(self, ctx):
if self.member.get_slow_sync("event"):
self.hashtable.set_slow_sync("event")
for i, event in enumerate(calendar_service.GetCalendarEventFeed().entry):
change = opensync.OSyncChange()
change.member = self.member
change.objtype = "event"
change.format = "vevent20"
change.data = self.event_to_data(event)
change.hash = hashlib.md5(change.data).hexdigest()
change.uid = "gcal-event-%d" % i
#print change.data
self.hashtable.detect_change(change)
if change.changetype != opensync.CHANGE_UNMODIFIED:
change.report(ctx)
self.hashtable.update_hash(change)
ctx.report_success()
def commit_change(self, ctx, chg):
#print chg.data
ctx.report_success()
def initialize(member):
return SyncClass(member)
def discover(info):
pass
def get_info(plugin):
""" hook: Get metadata """
plugin.accept_objtype("event")
plugin.accept_objformat("event", "xml-event")
plugin.name = "google-calendar-sync"
plugin.longname = "New google calendar sync plugin"
plugin.description = "Blub."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment