# HG changeset patch # User Matti Hamalainen # Date 1467625777 -10800 # Node ID 74f1725657524f0b584c18fc188c502492d8c22f # Parent c0e97f27f929f418236ce809fe755d80f9074492 Initial import. diff -r c0e97f27f929 -r 74f172565752 multimerge.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/multimerge.py Mon Jul 04 12:49:37 2016 +0300 @@ -0,0 +1,162 @@ +#!/usr/bin/python +import os +import sys +import signal +import re +import time +import datetime +import httplib2 +import ConfigParser +import oauth2client +from oauth2client import client +from oauth2client import tools +from googleapiclient import discovery + + +### +### Misc. helper functions +### + +## Wrapper for print() that does not break when redirecting stdin/out +## because of piped output not having a defined encoding. We default +## to UTF-8 encoding in output here. +def gcm_print(smsg): + gcm_msgbuf.append(smsg.encode("UTF-8")) + if sys.stdout.encoding != None: + print(smsg.encode(sys.stdout.encoding)) + else: + print(smsg.encode("UTF-8")) + + +## Fatal errors +def gcm_fatal(smsg): + gcm_print(u"ERROR: "+ smsg) + sys.exit(1) + + +## Debug messages +def gcm_debug(smsg): + if cfg.debug: + gcm_print(u"DBG: "+ smsg) + else: + gcm_msgbuf.append(u"DBG: "+ smsg.encode("UTF-8")) + + +## Handle SIGINT signals here +def gcm_signal_handler(signal, frame): + gcm_print("\nQuitting due to SIGINT / Ctrl+C!") + sys.exit(0) + + +def gcm_get_credentials(mcfg): + store = oauth2client.file.Storage(mcfg.credential_file) + credentials = store.get() + if not credentials or credentials.invalid: + flow = client.flow_from_clientsecrets(mcfg.secret_file, mcfg.scope) + flow.user_agent = mcfg.app_name + credentials = tools.run_flow(flow, store, mcfg) + if not credentials or credentials.invalid: + gcm_fatal("Failed to authenticate / invalid credentials.") + return credentials + + +def gcm_dump_events(events): + for event in events: + ev_start = event["start"].get("dateTime", event["start"].get("date")) + ev_end = event["end"].get("dateTime", event["end"].get("date")) + gcm_print(u"{0:25} - {1:25} : {2}".format(ev_start, ev_end, event["summary"])) + + +class GCMSettings(dict): + def __init__(self): + self.m_data = {} + self.m_saveable = {} + self.m_translate = {} + + def __getattr__(self, name): + if name in self.m_data: + return self.m_data[name] + else: + gcm_fatal("GCMSettings.__getattr__(): No such attribute '"+ name +"'.") + + def mtranslate(self, name, value): + if name in self.m_translate and self.m_translate[name]: + return self.m_translate[name](value) + else: + return value + + def mdef(self, name, saveable, validate, translate, value): + self.m_saveable[name] = saveable + self.m_data[name] = self.mtranslate(name, value) + + def mset(self, name, value): + if name in self.m_data: + self.m_data[name] = self.mtranslate(name, value) + else: + gcm_fatal("GCMSettings.mset(): No such attribute '"+ name +"'.") + + def mget(self, name): + if name in self.m_data: + return self.m_data[name] + else: + return None + + +### +### Main program starts +### +gcm_msgbuf = [] +signal.signal(signal.SIGINT, gcm_signal_handler) + + +## Settings +cfg = GCMSettings() + +cfg.mdef("debug", True, gcm_is_bool, gcm_trans_bool, False) + +cfg.mdef("source_regex", True, gcm_is_string, None, "^R:\s*(.*?)\s*\(\s*(.+?)\s*\)\s*$") +cfg.mdef("source_regmap", False, gcm_is_list, gcm_trans_list, [1, 2]) +cfg.mdef("source_regmap_len", False, None, None, len(cfg.source_regmap)) + +cfg.mdef("dest_name", True, gcm_is_string, None, u"Raahen kansainvälisyystoiminta") +cfg.mdef("dest_id", True, gcm_is_string, None, None) + +cfg.mdef("noauth_local_webserver", False, None, None, True) +#cfg.mdef("auth_host_name", False, None, None, "localhost") +#cfg.mdef("auth_host_port", False, None, None, [8080, 8090]) +cfg.mdef("logging_level", True, gcm_is_log_level, gcm_trans_log_level, "ERROR") + +# No need to touch these +cfg.mdef("app_name", False, None, None, "Google Calendar MultiMerge") +cfg.mdef("scope", False, None, None, "https://www.googleapis.com/auth/calendar") +#cfg.mdef("scope", False, None, None, "https://www.googleapis.com/auth/calendar.readonly") +cfg.mdef("secret_file", True, gcm_is_filename, None, "client_secret.json") +cfg.mdef("credential_file", True, gcm_is_filename, None, "client_credentials.json") + +## Initialize and authorize API connection +credentials = gcm_get_credentials(cfg) +http = credentials.authorize(httplib2.Http()) +service = discovery.build("calendar", "v3", http=http) + + +## Fetch complete calendar list +gcm_debug("Fetching available calendars ..") +calendars = [] +calPageToken = None +while True: + # We want everything except deleted and hidden calendars + calResult = service.calendarList().list( + showHidden=False, + showDeleted=False, + pageToken=calPageToken + ).execute() + + calendars.extend(calResult.get("items", [])) + calPageToken = calResult.get("nextPageToken") + if not calPageToken: + break + +if len(calendars) == 0: + gcm_fatal("No calendars found?") + +