import os
import pickle
import collections
+import argparse
import logging
logger = logging.getLogger('gooswapper')
logger.setLevel(logging.INFO)
return gcal_acct.events().get(calendarId=gcal_id,eventId=eventId).execute()
def get_gcal_recur_instance(gcal_acct,gcal_master,start,gcal_id="primary"):
+ if gcal_master is None:
+ logger.warning("Cannot get recurrences from event with null gcal id")
+ return None
ans = gcal_acct.events().instances(calendarId=gcal_id,
eventId=gcal_master,
originalStart=start.isoformat(),
password = getpass.getpass(prompt="Password for user %s: " % username)
return exchangelib.ServiceAccount(username,password)
-def ex_login(emailaddr,ad_cache_path=None):
+def ex_login(username,emailaddr,ad_cache_path=None):
global exchange_credential
autodiscover = True
if exchange_credential is None:
- exchange_credential = get_ex_cred()
+ exchange_credential = get_ex_cred(username)
if ad_cache_path is not None:
try:
with open(ad_cache_path,"rb") as f:
gcal_id="primary"):
for ev_id in changed:
event = get_ex_event_by_itemid(ex_acct.calendar,ev_id)
+ if event.gcal_link is None:
+ logger.warning("Cannot apply update where event has no gcal link")
+ continue
gevent = build_gcal_event_from_ex(event,gcal_tz)
if event.type=="RecurringMaster":
rr = rrule_from_ex(event,gcal_tz)
else:
logger.warning("Unable to set recurrence for %s" % event.item_id)
continue #don't make the gcal event
- gevent = gcal_acct.events().update(calendarId=gcal_id,
+ try: #may fail if we don't own the event
+ gevent = gcal_acct.events().update(calendarId=gcal_id,
eventId=event.gcal_link,
body=gevent,
sendUpdates="none").execute()
+ except googleapiclient.errors.HttpError as err:
+ if err.resp.status == 403:
+ pass
def match_ex_to_gcal(ex_acct,gcal_acct,gcal_tz,events,gcal_id="primary",ignore_link=True):
recur = 0
logger.info("Matched %d events, skipped %d with existing link, and %d recurring ones" % (matched,skipped,recur))
return toadd
-def get_gcal_cred():
+def get_gcal_cred(args):
#each such file can only store a single credential
storage = oauth2client.file.Storage(gcal_authpath)
gcal_credential = storage.get()
if gcal_credential is None or gcal_credential.invalid:
gcal_credential = oauth2client.tools.run_flow(flow,
storage,
- oauth2client.tools.argparser.parse_args())
+ args)
return gcal_credential
-def gcal_login():
- gcal_credential = get_gcal_cred()
+def gcal_login(args):
+ gcal_credential = get_gcal_cred(args)
# Object to handle http requests; could add proxy details
http = httplib2.Http()
http = gcal_credential.authorize(http)
return exchangelib.EWSTimeZone.timezone(gcal['timeZone'])
def main():
+ ap=argparse.ArgumentParser(description="Gooswapper calendar sync",
+ parents=[oauth2client.tools.argparser])
+ ap.add_argument("exchuser",help="Exchange user e.g. 'SANGER\mv3'")
+ ap.add_argument("exchemail",
+ help="Exchange calendar email e.g. ISGGroup@sanger.ac.uk")
+ ap.add_argument("-g","--gcalid",help="google Calendar ID")
+ ap.add_argument("-l","--loop",help="keep running indefinitely",
+ action="store_true")
+ args = ap.parse_args()
+ if args.gcalid is None:
+ gcal_id = "primary"
+ else:
+ gcal_id = args.gcalid
try:
with open(cachepath,"rb") as f:
cache = pickle.load(f)
except FileNotFoundError:
cache = None
- ex_account = ex_login("mv3@sanger.ac.uk",".gooswapper_exch_conf.dat")
+ ex_account = ex_login(args.exchuser,args.exchemail,
+ ".gooswapper_exch_conf.dat")
current = get_ex_events(ex_account.calendar)
- gcal_account = gcal_login()
- gcal_tz = get_gcal_timezone(gcal_account)
+ gcal_account = gcal_login(args)
+ gcal_tz = get_gcal_timezone(gcal_account,gcal_id)
if cache is not None:
added,deleted,changed = ex_event_changes(cache,current)
- add_ex_to_gcal(ex_account,gcal_account,gcal_tz,current,added)
+ add_ex_to_gcal(ex_account,gcal_account,gcal_tz,current,added,gcal_id)
#delete op needs the "cache" set, as that has the link ids in
#for events that are now deleted
- del_ex_to_gcal(ex_account,gcal_account,cache,deleted)
- update_ex_to_gcal(ex_account,gcal_account,gcal_tz,current,changed)
+ del_ex_to_gcal(ex_account,gcal_account,cache,deleted,gcal_id)
+ update_ex_to_gcal(ex_account,gcal_account,gcal_tz,current,
+ changed,gcal_id)
else:
- toadd = match_ex_to_gcal(ex_account,gcal_account,gcal_tz,current)
- add_ex_to_gcal(ex_account,gcal_account,gcal_tz,current,toadd)
+ toadd = match_ex_to_gcal(ex_account,gcal_account,gcal_tz,current,
+ gcal_id)
+ add_ex_to_gcal(ex_account,gcal_account,gcal_tz,current,toadd,gcal_id)
with open(cachepath,"wb") as f:
pickle.dump(current,f)