changeset 1:74f172565752

Initial import.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 04 Jul 2016 12:49:37 +0300
parents c0e97f27f929
children 34c3a08a4a37
files multimerge.py
diffstat 1 files changed, 162 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /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?")
+
+