Created
August 25, 2010 19:14
-
-
Save xqms/550096 to your computer and use it in GitHub Desktop.
Google calendar plugin for opensync
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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 <[email protected]> | |
""" | |
# 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