Mercurial > hg > gcmultimerge
annotate multimerge.py @ 134:afdef805e9b7
Merge Python 3 branch.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 15 Sep 2020 23:39:49 +0300 |
parents | 3a3958edc813 76e49e34b40a |
children | d3135eff1bab |
rev | line source |
---|---|
125
76e49e34b40a
Changes required for Python 3.
Matti Hamalainen <ccr@tnsp.org>
parents:
124
diff
changeset
|
1 #!/usr/bin/python3 |
3 | 2 # coding=utf-8 |
2 | 3 ### |
36 | 4 ### Google Calendar MultiMerge |
80
4bed40e35fae
Fix copyright and add information about license.
Matti Hamalainen <ccr@tnsp.org>
parents:
78
diff
changeset
|
5 ### Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> |
134 | 6 ### (C) Copyright 2016-2020 Tecnic Software productions (TNSP) |
80
4bed40e35fae
Fix copyright and add information about license.
Matti Hamalainen <ccr@tnsp.org>
parents:
78
diff
changeset
|
7 ### |
4bed40e35fae
Fix copyright and add information about license.
Matti Hamalainen <ccr@tnsp.org>
parents:
78
diff
changeset
|
8 ### For license information, see file "COPYING". |
2 | 9 ### |
134 | 10 ### Python 3.7 required! Please refer to |
2 | 11 ### README.txt for information on other depencies. |
12 ### | |
1 | 13 import os |
14 import sys | |
15 import signal | |
16 import re | |
45
035be8a9e982
Force reading of configuration in Unicode UTF-8.
Matti Hamalainen <ccr@tnsp.org>
parents:
44
diff
changeset
|
17 import codecs |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
18 import math |
73
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
19 import time |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
20 from subprocess import Popen, PIPE |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
21 |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
22 import smtplib |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
23 from email.mime.text import MIMEText |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
24 |
1 | 25 import httplib2 |
125
76e49e34b40a
Changes required for Python 3.
Matti Hamalainen <ccr@tnsp.org>
parents:
124
diff
changeset
|
26 import configparser as ConfigParser |
20 | 27 |
1 | 28 import oauth2client |
29 from oauth2client import client | |
30 from oauth2client import tools | |
4 | 31 from oauth2client import file |
1 | 32 from googleapiclient import discovery |
33 | |
34 | |
134 | 35 assert sys.version_info >= (3, 7) |
36 | |
37 | |
1 | 38 ### |
120 | 39 ### Misc. helper functions, etc |
1 | 40 ### |
120 | 41 |
42 ## List of event tuple fields that should NOT be compared for equality | |
66
b08d159e35c7
Sanitize gcm_no_compare_fields[]
Matti Hamalainen <ccr@tnsp.org>
parents:
65
diff
changeset
|
43 gcm_no_compare_fields = [ |
b08d159e35c7
Sanitize gcm_no_compare_fields[]
Matti Hamalainen <ccr@tnsp.org>
parents:
65
diff
changeset
|
44 "id", "iCalUID", "etag", "sequence", "gcm_cal_id", |
72
1ab40033bb87
Add 'creator' to event equality comparision excluded fields.
Matti Hamalainen <ccr@tnsp.org>
parents:
71
diff
changeset
|
45 "created", "updated", "htmlLink", "organizer", "creator", |
132
16404eaf35df
Exclude "extendedProperties property.
Matti Hamalainen <ccr@tnsp.org>
parents:
131
diff
changeset
|
46 "extendedProperties", |
66
b08d159e35c7
Sanitize gcm_no_compare_fields[]
Matti Hamalainen <ccr@tnsp.org>
parents:
65
diff
changeset
|
47 ] |
b08d159e35c7
Sanitize gcm_no_compare_fields[]
Matti Hamalainen <ccr@tnsp.org>
parents:
65
diff
changeset
|
48 |
120 | 49 ## List of logging levels from lowest to highest |
76
a63cc3633adb
Put log levels array into its own global variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
75
diff
changeset
|
50 gcm_log_levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"] |
a63cc3633adb
Put log levels array into its own global variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
75
diff
changeset
|
51 |
1 | 52 |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
53 def gcm_get_log_level(): |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
54 return gcm_log_levels.index(cfg.logging_level) |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
55 |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
56 |
129
31e3100b5ed9
Add timestamps to messages.
Matti Hamalainen <ccr@tnsp.org>
parents:
127
diff
changeset
|
57 ## Return a formatted timestamp string |
31e3100b5ed9
Add timestamps to messages.
Matti Hamalainen <ccr@tnsp.org>
parents:
127
diff
changeset
|
58 def gcm_timestamp(stamp): |
31e3100b5ed9
Add timestamps to messages.
Matti Hamalainen <ccr@tnsp.org>
parents:
127
diff
changeset
|
59 return time.strftime("%Y-%m-%d %H:%M:%S", stamp) |
31e3100b5ed9
Add timestamps to messages.
Matti Hamalainen <ccr@tnsp.org>
parents:
127
diff
changeset
|
60 #.decode(locale.getlocale()[1]) |
31e3100b5ed9
Add timestamps to messages.
Matti Hamalainen <ccr@tnsp.org>
parents:
127
diff
changeset
|
61 |
31e3100b5ed9
Add timestamps to messages.
Matti Hamalainen <ccr@tnsp.org>
parents:
127
diff
changeset
|
62 |
1 | 63 ## Wrapper for print() that does not break when redirecting stdin/out |
64 ## because of piped output not having a defined encoding. We default | |
65 ## to UTF-8 encoding in output here. | |
66 def gcm_print(smsg): | |
47 | 67 gcm_msgbuf.append(smsg) |
134 | 68 print("{0} | {1}".format(gcm_timestamp(time.localtime()), smsg)) |
1 | 69 |
70 | |
120 | 71 ## Fatal error handler |
1 | 72 def gcm_fatal(smsg): |
73 gcm_print(u"ERROR: "+ smsg) | |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
74 if cfg.email_ok and cfg.email != "off": |
120 | 75 ## If e-mail is not "off", send e-mail |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
76 msg = MIMEText(("\n".join(gcm_msgbuf)).encode("UTF-8"), "plain") |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
77 msg.set_charset("UTF-8") |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
78 msg["Subject"] = cfg.email_subject |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
79 msg["From"] = cfg.email_sender |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
80 msg["To"] = ",".join(cfg.email_to) |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
81 gcm_print("Sending mail to {0} from {1}, subj: {2} ..".format(";".join(cfg.email_to), cfg.email_sender, cfg.email_subject)) |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
82 try: |
120 | 83 # Act based on email mode |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
84 if cfg.email == "smtp": |
127 | 85 # Connect via SMTP |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
86 gcm_print("Using SMTP server {0}, login {1}".format(cfg.email_smtp_server, cfg.email_smtp_user)) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
87 server = smtplib.SMTP(cfg.email_smtp_server) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
88 if gcm_check_debug(4): |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
89 server.set_debuglevel(10) |
95
b5cc76f18256
Fix e-mail sending and implement basic TLS support for outgoing SMTP.
Matti Hamalainen <ccr@tnsp.org>
parents:
93
diff
changeset
|
90 |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
91 if cfg.email_smtp_tls: |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
92 server.starttls() |
95
b5cc76f18256
Fix e-mail sending and implement basic TLS support for outgoing SMTP.
Matti Hamalainen <ccr@tnsp.org>
parents:
93
diff
changeset
|
93 |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
94 server.login(cfg.email_smtp_user, cfg.email_smtp_password) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
95 server.sendmail(cfg.email_sender, cfg.email_to, msg.as_string()) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
96 server.quit() |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
97 elif cfg.email == "sendmail": |
127 | 98 # Use local sendmail |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
99 gcm_print("Using sendmail {0}".format(cfg.email_sendmail)) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
100 p = Popen([cfg.email_sendmail, "-t", "-oi"], stdin=PIPE) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
101 p.communicate(msg.as_string()) |
70
d5e3ca4b609d
Improve error handling when e-mail sending fails.
Matti Hamalainen <ccr@tnsp.org>
parents:
68
diff
changeset
|
102 except Exception as e: |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
103 gcm_print(u"FATAL: Oh noes, e-mail sending failed: {0}".format(str(e))) |
1 | 104 sys.exit(1) |
105 | |
106 | |
107 ## Debug messages | |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
108 def gcm_check_debug(level): |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
109 return cfg.debug and gcm_get_log_level() >= level |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
110 |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
111 def gcm_debug(level, smsg): |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
112 if gcm_check_debug(level): |
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
113 gcm_print(u"DBG: {0}".format(smsg)) |
1 | 114 else: |
47 | 115 gcm_msgbuf.append(u"DBG: {0}".format(smsg)) |
1 | 116 |
117 | |
24 | 118 ## Handler for SIGINT signals |
1 | 119 def gcm_signal_handler(signal, frame): |
54 | 120 gcm_print(u"\nQuitting due to SIGINT / Ctrl+C!") |
1 | 121 sys.exit(0) |
122 | |
123 | |
24 | 124 ## Function for handling Google API credentials |
99
4b84bb5bb8b5
Make credential_file and secret_file arguments to gcm_get_credentials().
Matti Hamalainen <ccr@tnsp.org>
parents:
98
diff
changeset
|
125 def gcm_get_credentials(mcfg, credential_file, secret_file): |
42 | 126 try: |
99
4b84bb5bb8b5
Make credential_file and secret_file arguments to gcm_get_credentials().
Matti Hamalainen <ccr@tnsp.org>
parents:
98
diff
changeset
|
127 store = oauth2client.file.Storage(credential_file) |
42 | 128 except Exception as e: |
99
4b84bb5bb8b5
Make credential_file and secret_file arguments to gcm_get_credentials().
Matti Hamalainen <ccr@tnsp.org>
parents:
98
diff
changeset
|
129 gcm_fatal(u"Failed to read credential file:\n{0}\n\nERROR: {1}\n".format(credential_file, str(e))) |
42 | 130 |
1 | 131 credentials = store.get() |
132 if not credentials or credentials.invalid: | |
42 | 133 try: |
99
4b84bb5bb8b5
Make credential_file and secret_file arguments to gcm_get_credentials().
Matti Hamalainen <ccr@tnsp.org>
parents:
98
diff
changeset
|
134 flow = client.flow_from_clientsecrets(secret_file, mcfg.scope) |
42 | 135 except Exception as e: |
99
4b84bb5bb8b5
Make credential_file and secret_file arguments to gcm_get_credentials().
Matti Hamalainen <ccr@tnsp.org>
parents:
98
diff
changeset
|
136 gcm_fatal(u"Failed to fetch client secret:\n{0}\n\nERROR: {1}\n".format(secret_file, str(e))) |
42 | 137 |
1 | 138 flow.user_agent = mcfg.app_name |
139 credentials = tools.run_flow(flow, store, mcfg) | |
140 if not credentials or credentials.invalid: | |
65 | 141 gcm_fatal(u"Failed to authenticate / invalid credentials.") |
1 | 142 return credentials |
143 | |
144 | |
120 | 145 ## Dump/print a given list of events for debugging purposes |
103
fc361e368630
Improve gcm_dump_events() further by changing show parameter to a lambda
Matti Hamalainen <ccr@tnsp.org>
parents:
102
diff
changeset
|
146 def gcm_dump_events(events, show): |
1 | 147 for event in events: |
103
fc361e368630
Improve gcm_dump_events() further by changing show parameter to a lambda
Matti Hamalainen <ccr@tnsp.org>
parents:
102
diff
changeset
|
148 if show == None or show(event): |
101
b5c381f9b51f
Improve gcm_dump_events() to optionally show all events (deleted/cancelled).
Matti Hamalainen <ccr@tnsp.org>
parents:
100
diff
changeset
|
149 ev_start = event["start"].get("dateTime", event["start"].get("date")) if "start" in event else "?" |
b5c381f9b51f
Improve gcm_dump_events() to optionally show all events (deleted/cancelled).
Matti Hamalainen <ccr@tnsp.org>
parents:
100
diff
changeset
|
150 ev_end = event["end"].get("dateTime", event["end"].get("date")) if "end" in event else "?" |
b5c381f9b51f
Improve gcm_dump_events() to optionally show all events (deleted/cancelled).
Matti Hamalainen <ccr@tnsp.org>
parents:
100
diff
changeset
|
151 summary = event["summary"] if "summary" in event else "?" |
b5c381f9b51f
Improve gcm_dump_events() to optionally show all events (deleted/cancelled).
Matti Hamalainen <ccr@tnsp.org>
parents:
100
diff
changeset
|
152 status = "*" if event["status"] != u"cancelled" else "!" |
104
f6274227114b
Make debug/error messages more consistent.
Matti Hamalainen <ccr@tnsp.org>
parents:
103
diff
changeset
|
153 gcm_print(u"[{0}] {1:25} - {2:25} : {3} [{4}] [{5}]".format(status, ev_start, ev_end, summary, event["iCalUID"], event["id"])) |
1 | 154 |
56 | 155 |
156 ## Generate gcm IDs for given list of events | |
111
8b773358ad47
Use "id" field in source calendar events gcm_id generation, and "iCalUID" for the target calendar.
Matti Hamalainen <ccr@tnsp.org>
parents:
110
diff
changeset
|
157 def gcm_generate_ids(events, calendar_id, sep, field): |
26
1267d61f6224
Add function for generating unique internal IDs.
Matti Hamalainen <ccr@tnsp.org>
parents:
25
diff
changeset
|
158 if not events: |
1267d61f6224
Add function for generating unique internal IDs.
Matti Hamalainen <ccr@tnsp.org>
parents:
25
diff
changeset
|
159 return events |
1267d61f6224
Add function for generating unique internal IDs.
Matti Hamalainen <ccr@tnsp.org>
parents:
25
diff
changeset
|
160 |
111
8b773358ad47
Use "id" field in source calendar events gcm_id generation, and "iCalUID" for the target calendar.
Matti Hamalainen <ccr@tnsp.org>
parents:
110
diff
changeset
|
161 for event in events: |
8b773358ad47
Use "id" field in source calendar events gcm_id generation, and "iCalUID" for the target calendar.
Matti Hamalainen <ccr@tnsp.org>
parents:
110
diff
changeset
|
162 event["gcm_cal_id"] = calendar_id |
8b773358ad47
Use "id" field in source calendar events gcm_id generation, and "iCalUID" for the target calendar.
Matti Hamalainen <ccr@tnsp.org>
parents:
110
diff
changeset
|
163 event["gcm_id"] = calendar_id + sep + event[field] |
26
1267d61f6224
Add function for generating unique internal IDs.
Matti Hamalainen <ccr@tnsp.org>
parents:
25
diff
changeset
|
164 |
1267d61f6224
Add function for generating unique internal IDs.
Matti Hamalainen <ccr@tnsp.org>
parents:
25
diff
changeset
|
165 return events |
1267d61f6224
Add function for generating unique internal IDs.
Matti Hamalainen <ccr@tnsp.org>
parents:
25
diff
changeset
|
166 |
1 | 167 |
53
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
168 ## Find event by its gcm_id from given list or return None if not found |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
169 def gcm_get_event_by_gcm_id(list, id): |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
170 for event in list: |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
171 if event["gcm_id"] == id: |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
172 return event |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
173 return None |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
174 |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
175 |
120 | 176 ## Compare two given events for equality (except for excluded list of fields) |
53
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
177 def gcm_compare_events(ev1, ev2): |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
178 for field in ev1: |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
179 if not field in gcm_no_compare_fields and ev1[field] != ev2[field]: |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
180 return False |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
181 return True |
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
182 |
63 | 183 |
110
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
184 ## Fetch events for given calendar |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
185 def gcm_fetch_events(calendarId, showDeleted): |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
186 events = [] |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
187 ev_token = None |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
188 while True: |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
189 try: |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
190 result = service.events().list( |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
191 calendarId=calendarId, |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
192 showDeleted=showDeleted, |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
193 singleEvents=False, |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
194 pageToken=ev_token, |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
195 ).execute() |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
196 except Exception as e: |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
197 gcm_fatal(u"Failed to fetch calendar events for {0}:\n\nERROR: {1}\n".format(calendarId, str(e))) |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
198 |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
199 events.extend(result.get("items", [])) |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
200 ev_token = result.get("nextPageToken") |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
201 if not ev_token: |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
202 break |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
203 |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
204 return events |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
205 |
c6771a596d77
Add gcm_fetch_events() function that properly implements chained event list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
109
diff
changeset
|
206 |
67 | 207 ### |
208 ### Class for parsing and manipulating RGB colors | |
209 ### | |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
210 class GCMColor(): |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
211 def __init__(self, src = None): |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
212 if src == None: |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
213 self.r = self.g = self.b = 0 |
125
76e49e34b40a
Changes required for Python 3.
Matti Hamalainen <ccr@tnsp.org>
parents:
124
diff
changeset
|
214 elif isinstance(src, str): |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
215 if len(src) == 6: |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
216 self.r = int(src[0:2], 16) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
217 self.g = int(src[2:4], 16) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
218 self.b = int(src[4:6], 16) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
219 elif len(src) == 7 and src[0] == "#": |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
220 self.r = int(src[1:3], 16) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
221 self.g = int(src[3:5], 16) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
222 self.b = int(src[6:7], 16) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
223 else: |
65 | 224 gcm_fatal(u"Expected hex-triplet string for GCMColor() initializer: {0}".format(src)) |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
225 elif isinstance(src, GCMColor): |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
226 self.r = src.r |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
227 self.g = src.g |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
228 self.b = src.b |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
229 else: |
65 | 230 gcm_fatal(u"Invalid initializer for GCMColor() object.") |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
231 |
106
205767356d2c
Add two helper functions to GCMColor class.
Matti Hamalainen <ccr@tnsp.org>
parents:
105
diff
changeset
|
232 def to_hexrgb(): |
205767356d2c
Add two helper functions to GCMColor class.
Matti Hamalainen <ccr@tnsp.org>
parents:
105
diff
changeset
|
233 return "{0:02X}{1:02X}{2:02X}".format(self.r, self.g, self.b) |
205767356d2c
Add two helper functions to GCMColor class.
Matti Hamalainen <ccr@tnsp.org>
parents:
105
diff
changeset
|
234 |
205767356d2c
Add two helper functions to GCMColor class.
Matti Hamalainen <ccr@tnsp.org>
parents:
105
diff
changeset
|
235 def to_hexrgb_lc(): |
205767356d2c
Add two helper functions to GCMColor class.
Matti Hamalainen <ccr@tnsp.org>
parents:
105
diff
changeset
|
236 return "{0:02x}{1:02x}{2:02x}".format(self.r, self.g, self.b) |
205767356d2c
Add two helper functions to GCMColor class.
Matti Hamalainen <ccr@tnsp.org>
parents:
105
diff
changeset
|
237 |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
238 def delta(self, other): |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
239 ctmp = GCMColor() |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
240 ctmp.r = other.r - self.r |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
241 ctmp.g = other.g - self.g |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
242 ctmp.b = other.b - self.b |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
243 return ctmp |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
244 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
245 def dist(self, other): |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
246 ctmp = self.delta(other) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
247 return math.sqrt(ctmp.r * ctmp.r + ctmp.g * ctmp.g + ctmp.b * ctmp.b) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
248 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
249 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
250 def gcm_find_nearest_color(colors, cfind, maxdist): |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
251 c_fg = GCMColor(cfind["foreground"]) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
252 c_bg = GCMColor(cfind["background"]) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
253 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
254 bdist_fg = 99999999999 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
255 bdist_bg = 99999999999 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
256 best_fit = None |
125
76e49e34b40a
Changes required for Python 3.
Matti Hamalainen <ccr@tnsp.org>
parents:
124
diff
changeset
|
257 for id, col in colors.items(): |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
258 dist_fg = GCMColor(col["foreground"]).dist(c_fg) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
259 dist_bg = GCMColor(col["background"]).dist(c_bg) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
260 if dist_fg <= bdist_fg and dist_bg <= bdist_bg: |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
261 best_fit = id |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
262 bdist_fg = dist_fg |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
263 bdist_bg = dist_bg |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
264 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
265 if bdist_fg <= maxdist and bdist_bg <= maxdist: |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
266 return best_fit |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
267 else: |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
268 return None |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
269 |
53
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
270 |
24 | 271 ## |
272 ## Class for handling configuration / settings | |
273 ## | |
1 | 274 class GCMSettings(dict): |
275 def __init__(self): | |
276 self.m_data = {} | |
92
a348dfa6b132
Rename a variable/class member.
Matti Hamalainen <ccr@tnsp.org>
parents:
89
diff
changeset
|
277 self.m_settable = {} |
5
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
278 self.m_validate = {} |
1 | 279 self.m_translate = {} |
280 | |
281 def __getattr__(self, name): | |
282 if name in self.m_data: | |
283 return self.m_data[name] | |
284 else: | |
65 | 285 gcm_fatal(u"GCMSettings.__getattr__(): No such attribute '"+ name +"'.") |
1 | 286 |
5
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
287 def mvalidate(self, name, value): |
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
288 if name in self.m_validate and self.m_validate[name]: |
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
289 if not self.m_validate[name](value): |
65 | 290 gcm_fatal(u"GCMSettings.mvalidate(): Invalid value for attribute '{0}': {1}".format(name, value)) |
5
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
291 |
1 | 292 def mtranslate(self, name, value): |
293 if name in self.m_translate and self.m_translate[name]: | |
294 return self.m_translate[name](value) | |
295 else: | |
296 return value | |
297 | |
92
a348dfa6b132
Rename a variable/class member.
Matti Hamalainen <ccr@tnsp.org>
parents:
89
diff
changeset
|
298 def mdef(self, name, settable, validate, translate, value): |
5
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
299 self.mvalidate(name, value) |
92
a348dfa6b132
Rename a variable/class member.
Matti Hamalainen <ccr@tnsp.org>
parents:
89
diff
changeset
|
300 self.m_settable[name] = settable |
5
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
301 self.m_validate[name] = validate |
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
302 self.m_translate[name] = translate |
1 | 303 self.m_data[name] = self.mtranslate(name, value) |
304 | |
305 def mset(self, name, value): | |
5
9d4152f32223
Add some code for settings validation.
Matti Hamalainen <ccr@tnsp.org>
parents:
4
diff
changeset
|
306 self.mvalidate(name, value) |
1 | 307 if name in self.m_data: |
308 self.m_data[name] = self.mtranslate(name, value) | |
309 else: | |
43 | 310 gcm_fatal(u"GCMSettings.mset(): No such attribute '"+ name +"'.") |
1 | 311 |
312 def mget(self, name): | |
313 if name in self.m_data: | |
314 return self.m_data[name] | |
315 else: | |
316 return None | |
317 | |
119
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
318 def mread(self, cfg_parser, sect): |
92
a348dfa6b132
Rename a variable/class member.
Matti Hamalainen <ccr@tnsp.org>
parents:
89
diff
changeset
|
319 for name in self.m_settable: |
119
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
320 if cfg_parser.has_option(sect, name): |
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
321 value = cfg_parser.get(sect, name) |
6
ee6bf617f839
Implement configuration file reading.
Matti Hamalainen <ccr@tnsp.org>
parents:
5
diff
changeset
|
322 self.mset(name, value) |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
323 gcm_debug(4, u"{0} -> '{1}' == {2}".format(name, value, self.mget(name))) |
6
ee6bf617f839
Implement configuration file reading.
Matti Hamalainen <ccr@tnsp.org>
parents:
5
diff
changeset
|
324 |
23 | 325 def is_str(self, mvalue): |
125
76e49e34b40a
Changes required for Python 3.
Matti Hamalainen <ccr@tnsp.org>
parents:
124
diff
changeset
|
326 return isinstance(mvalue, str) |
23 | 327 |
328 def is_string(self, mvalue): | |
329 return mvalue == None or self.is_str(mvalue) | |
330 | |
331 def is_log_level(self, mvalue): | |
332 if not self.is_str(mvalue): | |
333 return False | |
334 else: | |
76
a63cc3633adb
Put log levels array into its own global variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
75
diff
changeset
|
335 return mvalue.upper() in gcm_log_levels |
23 | 336 |
337 def trans_log_level(self, mvalue): | |
338 return mvalue.upper() | |
339 | |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
340 def is_email_state(self, mvalue): |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
341 if not self.is_str(mvalue): |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
342 return False |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
343 else: |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
344 return mvalue.lower() in [u"off", u"sendmail", u"smtp"] |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
345 |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
346 def trans_email_state(self, mvalue): |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
347 return mvalue.lower() |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
348 |
23 | 349 def is_filename(self, mvalue): |
350 if not self.is_str(mvalue): | |
351 return False | |
352 else: | |
353 return re.match("^[a-z0-9][a-z0-9\.\_\-]+$", mvalue, flags=re.IGNORECASE) | |
354 | |
355 def trans_bool(self, mvalue): | |
356 if self.is_str(mvalue): | |
357 if re.match("^\s*(true|1|on|yes)\s*$", mvalue, re.IGNORECASE): | |
358 mvalue = True | |
359 elif re.match("^\s*(false|0|off|no)\s*$", mvalue, re.IGNORECASE): | |
360 mvalue = False | |
361 else: | |
362 return None | |
363 return mvalue | |
364 | |
365 def is_bool(self, mvalue): | |
366 mval = self.trans_bool(mvalue) | |
367 if not isinstance(mval, bool): | |
65 | 368 gcm_fatal(u"GCMSettings.is_bool(): Invalid boolean value '{0}', should be true|false|1|0|on|off|yes|no.".format(mvalue)) |
23 | 369 else: |
370 return True | |
371 | |
372 def trans_list(self, mvalue): | |
373 morig = mvalue | |
374 if self.is_str(mvalue): | |
375 mvalue = re.split("\s*,\s*", mvalue, flags=re.IGNORECASE) | |
376 if not isinstance(mvalue, list): | |
65 | 377 gcm_fatal(u"GCMSettings.trans_list(): Could not parse list '{0}'.".format(mvalue)) |
23 | 378 elif not isinstance(mvalue, list): |
65 | 379 gcm_fatal(u"GCMSettings.trans_list(): Invalid value '{0}'.".format(mvalue)) |
23 | 380 return mvalue |
381 | |
382 def is_list(self, mvalue): | |
383 return self.trans_list(mvalue) | |
384 | |
385 def is_email(self, mvalue): | |
386 if not self.is_string(mvalue): | |
387 return False | |
388 else: | |
389 return re.match("^.*?\s+<[a-z0-9]+[a-z0-9\.\+\-]*\@[a-z0-9]+[a-z0-9\.\-]+>\s*$|[a-z0-9]+[a-z0-9\.\+\-]*\@[a-z0-9]+[a-z0-9\.\-]+", mvalue, flags=re.IGNORECASE) | |
390 | |
391 def trans_email_list(self, mvalue): | |
392 if mvalue == None: | |
393 return mvalue | |
394 else: | |
395 return self.trans_list(mvalue.strip()) | |
396 | |
397 def is_email_list(self, mvalue): | |
398 mvalue = self.trans_email_list(mvalue) | |
399 if mvalue != None: | |
400 for email in mvalue: | |
401 if not self.is_email(email): | |
65 | 402 gcm_fatal(u"Invalid e-mail address '{0}' in list {1}.".format(email, ", ".join(mvalue))) |
23 | 403 return True |
404 | |
1 | 405 |
406 ### | |
407 ### Main program starts | |
408 ### | |
409 gcm_msgbuf = [] | |
410 signal.signal(signal.SIGINT, gcm_signal_handler) | |
411 | |
87
c3f8af496f46
Add "execution time elapsed" metering.
Matti Hamalainen <ccr@tnsp.org>
parents:
86
diff
changeset
|
412 gcm_bench_start = time.time() |
c3f8af496f46
Add "execution time elapsed" metering.
Matti Hamalainen <ccr@tnsp.org>
parents:
86
diff
changeset
|
413 |
1 | 414 |
120 | 415 ## Define all the settings |
119
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
416 cfg_section = "gcm" |
1 | 417 cfg = GCMSettings() |
418 | |
23 | 419 cfg.mdef("debug", True, cfg.is_bool, cfg.trans_bool, False) |
1 | 420 |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
421 cfg.mdef("email_ok", False, None, None, False) |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
422 cfg.mdef("email", True, cfg.is_email_state, cfg.trans_email_state, u"off") |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
423 |
23 | 424 cfg.mdef("email_to", True, cfg.is_email_list, cfg.trans_email_list, None) |
425 cfg.mdef("email_sender", True, cfg.is_email, None, None) | |
49 | 426 cfg.mdef("email_subject", True, cfg.is_string, None, u"Google Calendar MultiMerge status") |
95
b5cc76f18256
Fix e-mail sending and implement basic TLS support for outgoing SMTP.
Matti Hamalainen <ccr@tnsp.org>
parents:
93
diff
changeset
|
427 |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
428 cfg.mdef("email_sendmail", True, cfg.is_string, None, "/usr/sbin/sendmail") |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
429 cfg.mdef("email_smtp_tls", True, cfg.is_bool, cfg.trans_bool, False) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
430 cfg.mdef("email_smtp_server", True, cfg.is_string, None, None) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
431 cfg.mdef("email_smtp_user", True, cfg.is_string, None, None) |
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
432 cfg.mdef("email_smtp_password", True, cfg.is_string, None, None) |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
433 |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
434 cfg.mdef("src_regex", True, cfg.is_string, None, u"^R:\s*(.*?)\s*\(\s*(.+?)\s*\)\s*$") |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
435 cfg.mdef("src_regmap", False, cfg.is_list, cfg.trans_list, [1, 2]) |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
436 cfg.mdef("src_regmap_len", False, None, None, len(cfg.src_regmap)) |
1 | 437 |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
438 cfg.mdef("dst_name", True, cfg.is_string, None, None) |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
439 cfg.mdef("dst_regex", True, cfg.is_string, None, None) |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
440 cfg.mdef("dst_id", True, cfg.is_string, None, None) |
1 | 441 |
442 cfg.mdef("noauth_local_webserver", False, None, None, True) | |
443 #cfg.mdef("auth_host_name", False, None, None, "localhost") | |
444 #cfg.mdef("auth_host_port", False, None, None, [8080, 8090]) | |
23 | 445 cfg.mdef("logging_level", True, cfg.is_log_level, cfg.trans_log_level, "ERROR") |
1 | 446 |
447 # No need to touch these | |
448 cfg.mdef("app_name", False, None, None, "Google Calendar MultiMerge") | |
449 cfg.mdef("scope", False, None, None, "https://www.googleapis.com/auth/calendar") | |
450 #cfg.mdef("scope", False, None, None, "https://www.googleapis.com/auth/calendar.readonly") | |
23 | 451 cfg.mdef("secret_file", True, cfg.is_filename, None, "client_secret.json") |
452 cfg.mdef("credential_file", True, cfg.is_filename, None, "client_credentials.json") | |
1 | 453 |
6
ee6bf617f839
Implement configuration file reading.
Matti Hamalainen <ccr@tnsp.org>
parents:
5
diff
changeset
|
454 |
120 | 455 ## Check if we have arguments |
89 | 456 if len(sys.argv) <= 1: |
457 gcm_fatal(u"No configuration file specified.\nUsage: {0} <configfile>".format(sys.argv[0])) | |
458 | |
459 | |
6
ee6bf617f839
Implement configuration file reading.
Matti Hamalainen <ccr@tnsp.org>
parents:
5
diff
changeset
|
460 ## Read, parse and validate configuration file |
89 | 461 gcm_debug(3, u"Reading configuration from '{0}'.".format(sys.argv[1])) |
462 try: | |
119
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
463 cfg_parser = ConfigParser.RawConfigParser() |
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
464 cfg_parser.readfp(codecs.open(sys.argv[1], "r", "UTF-8")) |
89 | 465 except Exception as e: |
466 gcm_fatal(u"Failed to read configuration file '{0}': {1}".format(sys.argv[1], str(e))) | |
6
ee6bf617f839
Implement configuration file reading.
Matti Hamalainen <ccr@tnsp.org>
parents:
5
diff
changeset
|
467 |
89 | 468 # Check that the required section exists |
119
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
469 if not cfg_parser.has_section(cfg_section): |
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
470 gcm_fatal(u"Invalid configuration, missing '{0}' section.".format(cfg_section)) |
7
f2ecfb3e04ee
Check that the required section exists in configuration.
Matti Hamalainen <ccr@tnsp.org>
parents:
6
diff
changeset
|
471 |
89 | 472 # Debug setting is a special case, we need to get it |
473 # set before everything else, so do it here .. | |
119
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
474 if cfg_parser.has_option(cfg_section, "debug"): |
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
475 cfg.mset("debug", cfg_parser.get(cfg_section, "debug")) |
10
b237b96602ad
We need to handle "debug" setting before other settings, so we need a
Matti Hamalainen <ccr@tnsp.org>
parents:
9
diff
changeset
|
476 |
89 | 477 # Parse the settings and validate |
119
f671602635b7
Rename some objects and variables.
Matti Hamalainen <ccr@tnsp.org>
parents:
114
diff
changeset
|
478 cfg.mread(cfg_parser, cfg_section) |
6
ee6bf617f839
Implement configuration file reading.
Matti Hamalainen <ccr@tnsp.org>
parents:
5
diff
changeset
|
479 |
8 | 480 |
481 ## Validate settings | |
113
f45115bfb17a
Implement sendmail mail sending support. Various e-mail related settings have also
Matti Hamalainen <ccr@tnsp.org>
parents:
111
diff
changeset
|
482 if cfg.email != "off": |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
483 if cfg.email_subject == None or len(cfg.email_subject) == 0: |
65 | 484 gcm_fatal(u"E-mail enabled but email_subject not set.") |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
485 elif cfg.email_sender == None: |
65 | 486 gcm_fatal(u"E-mail enabled but email_sender not set.") |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
487 elif cfg.email_to == None: |
65 | 488 gcm_fatal(u"E-mail enabled but email_to not set.") |
14
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
489 else: |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
490 cfg.mset("email_ok", True) |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
491 |
8262efacf3fb
Initial implementation of sending e-mail in fatal error cases.
Matti Hamalainen <ccr@tnsp.org>
parents:
13
diff
changeset
|
492 |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
493 if len(cfg.src_regmap) != cfg.src_regmap_len: |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
494 gcm_fatal(u"Setting src_regmap list must be {0} items.".format(cfg.src_regmap_len)) |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
495 else: |
127 | 496 # Force convert values to integers |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
497 try: |
124
977ecff4bd7d
Use list comprehensions instead of map().
Matti Hamalainen <ccr@tnsp.org>
parents:
121
diff
changeset
|
498 cfg.src_regmap = [int(x) for x in cfg.src_regmap] |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
499 except Exception as e: |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
500 gcm_fatal(u"Invalid src_regmap: {0}".format(str(e))) |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
501 |
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
502 |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
503 if not cfg.dst_regex and not cfg.dst_id: |
65 | 504 gcm_fatal(u"Target calendar ID or name required, but not set.") |
8 | 505 |
506 | |
1 | 507 ## Initialize and authorize API connection |
99
4b84bb5bb8b5
Make credential_file and secret_file arguments to gcm_get_credentials().
Matti Hamalainen <ccr@tnsp.org>
parents:
98
diff
changeset
|
508 credentials = gcm_get_credentials(cfg, cfg.credential_file, cfg.secret_file) |
1 | 509 http = credentials.authorize(httplib2.Http()) |
510 service = discovery.build("calendar", "v3", http=http) | |
511 | |
512 | |
513 ## Fetch complete calendar list | |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
514 gcm_debug(3, u"Fetching available calendars ..") |
1 | 515 calendars = [] |
19 | 516 cal_token = None |
1 | 517 while True: |
518 # We want everything except deleted and hidden calendars | |
109
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
519 try: |
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
520 result = service.calendarList().list( |
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
521 showHidden=False, |
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
522 showDeleted=False, |
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
523 pageToken=cal_token |
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
524 ).execute() |
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
525 except Exception as e: |
8cf20367a372
Add exception checking to calendar list fetching.
Matti Hamalainen <ccr@tnsp.org>
parents:
108
diff
changeset
|
526 gcm_fatal(u"Failed to fetch calendar list:\n\nERROR: {0}\n".format(str(e))) |
1 | 527 |
17 | 528 calendars.extend(result.get("items", [])) |
19 | 529 cal_token = result.get("nextPageToken") |
530 if not cal_token: | |
1 | 531 break |
532 | |
533 if len(calendars) == 0: | |
65 | 534 gcm_fatal(u"No calendars found?") |
1 | 535 |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
536 gcm_debug(3, u"{0} calendars total found.".format(len(calendars))) |
50
f8618bae162a
Improve debug messages and comments.
Matti Hamalainen <ccr@tnsp.org>
parents:
49
diff
changeset
|
537 |
1 | 538 |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
539 ## Filter desired SOURCE calendars based on specified regexp |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
540 src_re = re.compile(cfg.src_regex, re.UNICODE) |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
541 dst_re = re.compile(cfg.dst_regex, re.UNICODE) |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
542 src_calendars = [] |
60
37705c4a35e1
Show target calendar information in debug mode.
Matti Hamalainen <ccr@tnsp.org>
parents:
59
diff
changeset
|
543 dst_calendar = None |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
544 for calendar in calendars: |
51
54644b29a9a3
Match also summaryOverride attribute against the source calendar regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
50
diff
changeset
|
545 if u"summary" in calendar: |
50
f8618bae162a
Improve debug messages and comments.
Matti Hamalainen <ccr@tnsp.org>
parents:
49
diff
changeset
|
546 # Find destination calendar ID if not set |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
547 if not cfg.dst_id and dst_re.match(calendar["summary"]): |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
548 cfg.mset("dst_id", calendar["id"]) |
60
37705c4a35e1
Show target calendar information in debug mode.
Matti Hamalainen <ccr@tnsp.org>
parents:
59
diff
changeset
|
549 dst_calendar = calendar |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
550 elif cfg.dst_id and calendar["id"] == cfg.dst_id: |
86
d9e799a27ceb
Make cfg.dest_id actually work.
Matti Hamalainen <ccr@tnsp.org>
parents:
85
diff
changeset
|
551 dst_calendar = calendar |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
552 |
51
54644b29a9a3
Match also summaryOverride attribute against the source calendar regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
50
diff
changeset
|
553 # If summary or summaryOverride match the regexp, add calendar |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
554 mre = src_re.match(calendar["summary"]) |
52 | 555 if not mre and u"summaryOverride" in calendar: |
114
591cc558bbcd
Use summaryOverride field as summary for calendars where it is being matched
Matti Hamalainen <ccr@tnsp.org>
parents:
113
diff
changeset
|
556 mre = src_re.match(calendar["summaryOverride"]) |
591cc558bbcd
Use summaryOverride field as summary for calendars where it is being matched
Matti Hamalainen <ccr@tnsp.org>
parents:
113
diff
changeset
|
557 calendar["summary"] = calendar["summaryOverride"] |
52 | 558 |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
559 if mre: |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
560 calendar["gcm_title"] = mre.group(cfg.src_regmap[0]) |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
561 calendar["gcm_id"] = mre.group(cfg.src_regmap[1]) |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
562 src_calendars.append(calendar) |
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
563 |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
564 gcm_debug(3, u"{0} source calendars found.".format(len(src_calendars))) |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
565 |
50
f8618bae162a
Improve debug messages and comments.
Matti Hamalainen <ccr@tnsp.org>
parents:
49
diff
changeset
|
566 |
f8618bae162a
Improve debug messages and comments.
Matti Hamalainen <ccr@tnsp.org>
parents:
49
diff
changeset
|
567 ## Check if we have destination calendar ID |
60
37705c4a35e1
Show target calendar information in debug mode.
Matti Hamalainen <ccr@tnsp.org>
parents:
59
diff
changeset
|
568 if not dst_calendar: |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
569 gcm_fatal(u"Could not find target/destination calendar ID for '"+ cfg.dst_name +"'.") |
60
37705c4a35e1
Show target calendar information in debug mode.
Matti Hamalainen <ccr@tnsp.org>
parents:
59
diff
changeset
|
570 else: |
93 | 571 gcm_debug(3, u"Target calendar '{0}' [ ID: {1} ]".format(dst_calendar["summary"], dst_calendar["id"])) |
9
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
572 |
01c933dba120
Filter source calendars based on regexp.
Matti Hamalainen <ccr@tnsp.org>
parents:
8
diff
changeset
|
573 |
120 | 574 ## Fetch calendar colors data |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
575 try: |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
576 colors = service.colors().get().execute() |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
577 except Exception as e: |
65 | 578 gcm_fatal(u"Failed to fetch calendar color settings:\n\n{0}".format(str(e))) |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
579 |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
580 |
120 | 581 ## Now, fetch and collect events from source calendars |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
582 gcm_debug(3, u"Fetching calendar events .. ") |
19 | 583 src_events = [] |
11
fcdee7c04ed8
Implement fetching of source events.
Matti Hamalainen <ccr@tnsp.org>
parents:
10
diff
changeset
|
584 for calendar in src_calendars: |
97 | 585 gcm_debug(4, u"- {0} ({1})".format(calendar["id"], calendar["summary"])) |
120 | 586 |
587 # Find matching color from the source calendar for the event, if one has been set | |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
588 c_found = None |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
589 if "colorId" in calendar and calendar["colorId"] in colors["calendar"]: |
97 | 590 gcm_debug(4, u" Calendar color: {0}".format(colors["calendar"][calendar["colorId"]])) |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
591 c_found = gcm_find_nearest_color(colors["event"], colors["calendar"][calendar["colorId"]], 100) |
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
592 if c_found: |
97 | 593 gcm_debug(4, u" Found nearest event color ID: {0}, {1}".format(c_found, colors["event"][c_found])) |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
594 else: |
97 | 595 gcm_debug(4, u" No matching event color found!") |
62
4891ed8d77d5
Implement nearest matching color search via cubic distance, and use it for
Matti Hamalainen <ccr@tnsp.org>
parents:
61
diff
changeset
|
596 |
120 | 597 # Fetch and add events, if any, to main source events list |
111
8b773358ad47
Use "id" field in source calendar events gcm_id generation, and "iCalUID" for the target calendar.
Matti Hamalainen <ccr@tnsp.org>
parents:
110
diff
changeset
|
598 events = gcm_generate_ids(gcm_fetch_events(calendar["id"], False), calendar["id"], "___", "id") |
11
fcdee7c04ed8
Implement fetching of source events.
Matti Hamalainen <ccr@tnsp.org>
parents:
10
diff
changeset
|
599 if events: |
31
6becdaa5c45f
Set event colors (just sequentially now) based on which source calendar they belong to.
Matti Hamalainen <ccr@tnsp.org>
parents:
30
diff
changeset
|
600 for event in events: |
120 | 601 # Set summary and color for existing events |
102
7ab6beb4c709
Only generate summaries and colors for non-deleted/cancelled events.
Matti Hamalainen <ccr@tnsp.org>
parents:
101
diff
changeset
|
602 if event["status"] != u"cancelled": |
7ab6beb4c709
Only generate summaries and colors for non-deleted/cancelled events.
Matti Hamalainen <ccr@tnsp.org>
parents:
101
diff
changeset
|
603 if c_found != None: |
7ab6beb4c709
Only generate summaries and colors for non-deleted/cancelled events.
Matti Hamalainen <ccr@tnsp.org>
parents:
101
diff
changeset
|
604 event["colorId"] = c_found |
133
3a3958edc813
Check that the "summary" attribute exists.
Matti Hamalainen <ccr@tnsp.org>
parents:
132
diff
changeset
|
605 if "summary" in event: |
3a3958edc813
Check that the "summary" attribute exists.
Matti Hamalainen <ccr@tnsp.org>
parents:
132
diff
changeset
|
606 event["summary"] = u"[{1}] {0}".format(event["summary"], calendar["gcm_id"]) |
3a3958edc813
Check that the "summary" attribute exists.
Matti Hamalainen <ccr@tnsp.org>
parents:
132
diff
changeset
|
607 else: |
3a3958edc813
Check that the "summary" attribute exists.
Matti Hamalainen <ccr@tnsp.org>
parents:
132
diff
changeset
|
608 event["summary"] = u"[?] {0}".format(calendar["gcm_id"]) |
120 | 609 |
610 # Add to list of source events | |
21
f392d495c5b6
We need to extend a list, not append to it.
Matti Hamalainen <ccr@tnsp.org>
parents:
20
diff
changeset
|
611 src_events.extend(events) |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
612 if gcm_check_debug(4): |
103
fc361e368630
Improve gcm_dump_events() further by changing show parameter to a lambda
Matti Hamalainen <ccr@tnsp.org>
parents:
102
diff
changeset
|
613 gcm_dump_events(events, (lambda ev: ev["status"] != u"cancelled")) |
11
fcdee7c04ed8
Implement fetching of source events.
Matti Hamalainen <ccr@tnsp.org>
parents:
10
diff
changeset
|
614 |
fcdee7c04ed8
Implement fetching of source events.
Matti Hamalainen <ccr@tnsp.org>
parents:
10
diff
changeset
|
615 |
120 | 616 ## Fetch current events from the target |
84
16edb168daa1
No need to display target calendar ID twice.
Matti Hamalainen <ccr@tnsp.org>
parents:
81
diff
changeset
|
617 gcm_debug(3, u"Fetching current target calendar events.") |
111
8b773358ad47
Use "id" field in source calendar events gcm_id generation, and "iCalUID" for the target calendar.
Matti Hamalainen <ccr@tnsp.org>
parents:
110
diff
changeset
|
618 dst_events = gcm_generate_ids(gcm_fetch_events(cfg.dst_id, True), "", "", "iCalUID") |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
619 gcm_debug(3, u"Found {0} event(s).".format(len(dst_events))) |
58 | 620 |
13
dd240a7ad913
Fetch current events in destination calendar.
Matti Hamalainen <ccr@tnsp.org>
parents:
11
diff
changeset
|
621 |
120 | 622 ## Start populating/updating events .. |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
623 gcm_debug(3, u"Re-merging events to target calendar ..") |
124
977ecff4bd7d
Use list comprehensions instead of map().
Matti Hamalainen <ccr@tnsp.org>
parents:
121
diff
changeset
|
624 dst_ids = frozenset([x["gcm_id"] for x in dst_events]) |
977ecff4bd7d
Use list comprehensions instead of map().
Matti Hamalainen <ccr@tnsp.org>
parents:
121
diff
changeset
|
625 src_ids = frozenset([x["gcm_id"] for x in src_events]) |
27 | 626 |
81
ce02c0c00e64
Add debug information about how many events were new, updated or unchanged.
Matti Hamalainen <ccr@tnsp.org>
parents:
80
diff
changeset
|
627 evn_new = evn_updated = evn_unchanged = 0 |
ce02c0c00e64
Add debug information about how many events were new, updated or unchanged.
Matti Hamalainen <ccr@tnsp.org>
parents:
80
diff
changeset
|
628 |
27 | 629 for event in src_events: |
630 # Does the event exist already in the target? | |
55 | 631 if event["gcm_id"] in dst_ids: |
39
693db3f8cbe5
Begin importing event comparision stuff.
Matti Hamalainen <ccr@tnsp.org>
parents:
38
diff
changeset
|
632 # Check if event NEEDS updating .. aka compare data |
104
f6274227114b
Make debug/error messages more consistent.
Matti Hamalainen <ccr@tnsp.org>
parents:
103
diff
changeset
|
633 gcm_debug(4, u"Event {0} [{1}] exists, checking ..".format(event["id"], event["gcm_id"])) |
53
ea62e0ed05ae
Initial implementation of event comparision amd generally more clever about
Matti Hamalainen <ccr@tnsp.org>
parents:
52
diff
changeset
|
634 d_event = gcm_get_event_by_gcm_id(dst_events, event["gcm_id"]) |
55 | 635 if not gcm_compare_events(event, d_event): |
57 | 636 # Seems we need to update |
104
f6274227114b
Make debug/error messages more consistent.
Matti Hamalainen <ccr@tnsp.org>
parents:
103
diff
changeset
|
637 gcm_debug(4, u"Updating event {0} [{1}]".format(event["id"], event["gcm_id"])) |
39
693db3f8cbe5
Begin importing event comparision stuff.
Matti Hamalainen <ccr@tnsp.org>
parents:
38
diff
changeset
|
638 try: |
121 | 639 # We need to remove the sequence and id fields, as they will be replaced by target |
55 | 640 event.pop("sequence", None) |
63 | 641 event.pop("id", None) |
642 event["iCalUID"] = event["gcm_id"] | |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
643 new_event = service.events().update(calendarId=cfg.dst_id, eventId=d_event["id"], body=event).execute() |
105
481cfea49e19
Update counters only per successful operation.
Matti Hamalainen <ccr@tnsp.org>
parents:
104
diff
changeset
|
644 evn_updated += 1 |
39
693db3f8cbe5
Begin importing event comparision stuff.
Matti Hamalainen <ccr@tnsp.org>
parents:
38
diff
changeset
|
645 except Exception as e: |
104
f6274227114b
Make debug/error messages more consistent.
Matti Hamalainen <ccr@tnsp.org>
parents:
103
diff
changeset
|
646 gcm_fatal(u"Failed to update event {0} [{1}]:\n\n{2}\n\nERROR: {3}\n".format(event["id"], event["gcm_id"], event, str(e))) |
39
693db3f8cbe5
Begin importing event comparision stuff.
Matti Hamalainen <ccr@tnsp.org>
parents:
38
diff
changeset
|
647 else: |
81
ce02c0c00e64
Add debug information about how many events were new, updated or unchanged.
Matti Hamalainen <ccr@tnsp.org>
parents:
80
diff
changeset
|
648 evn_unchanged += 1 |
104
f6274227114b
Make debug/error messages more consistent.
Matti Hamalainen <ccr@tnsp.org>
parents:
103
diff
changeset
|
649 gcm_debug(4, u"No need to update event {0} [{1}]".format(event["id"], event["gcm_id"])) |
131 | 650 elif event["status"] not in [u"cancelled", u"confirmed"]: |
120 | 651 # Event does not seem to exist. Insert new event. |
104
f6274227114b
Make debug/error messages more consistent.
Matti Hamalainen <ccr@tnsp.org>
parents:
103
diff
changeset
|
652 gcm_debug(4, u"Inserting new event {0} [{1}]".format(event["id"], event["gcm_id"])) |
121 | 653 # Remove original id field, otherwise it will clash |
63 | 654 event.pop("id", None) |
655 event["iCalUID"] = event["gcm_id"] # Replace Google generated ID with our own | |
34 | 656 try: |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
657 new_event = service.events().insert(calendarId=cfg.dst_id, body=event).execute() |
105
481cfea49e19
Update counters only per successful operation.
Matti Hamalainen <ccr@tnsp.org>
parents:
104
diff
changeset
|
658 evn_new += 1 |
34 | 659 except Exception as e: |
65 | 660 gcm_fatal(u"Failed to insert new event:\n\n{0}\n\nERROR: {1}\n".format(event, str(e))) |
63 | 661 |
81
ce02c0c00e64
Add debug information about how many events were new, updated or unchanged.
Matti Hamalainen <ccr@tnsp.org>
parents:
80
diff
changeset
|
662 gcm_debug(3, "{0} new events, {1} updated, {2} unchanged.".format(evn_new, evn_updated, evn_unchanged)) |
ce02c0c00e64
Add debug information about how many events were new, updated or unchanged.
Matti Hamalainen <ccr@tnsp.org>
parents:
80
diff
changeset
|
663 |
34 | 664 |
665 ## Remove "stale" events | |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
666 gcm_debug(3, u"Purging stale events ..") |
85
da09a3b1edaa
Count and report number of purged events.
Matti Hamalainen <ccr@tnsp.org>
parents:
84
diff
changeset
|
667 evn_purged = 0 |
38
54405de302d0
Attempt to delete stale events. Still needs a check for already deleted
Matti Hamalainen <ccr@tnsp.org>
parents:
37
diff
changeset
|
668 for event in dst_events: |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
669 gcm_debug(4, u"Checking event {0}".format(event["gcm_id"])) |
55 | 670 if not event["gcm_id"] in src_ids and event["status"] != u"cancelled": |
104
f6274227114b
Make debug/error messages more consistent.
Matti Hamalainen <ccr@tnsp.org>
parents:
103
diff
changeset
|
671 gcm_debug(4, u"Deleting event {0} [{1}]".format(event["id"], event["gcm_id"])) |
85
da09a3b1edaa
Count and report number of purged events.
Matti Hamalainen <ccr@tnsp.org>
parents:
84
diff
changeset
|
672 evn_purged += 1 |
63 | 673 try: |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
674 service.events().delete(calendarId=cfg.dst_id, eventId=event["id"]).execute() |
63 | 675 except Exception as e: |
65 | 676 gcm_fatal(u"Failed to delete stale event:\n{0}\n\nERROR: {1}\n".format(event, str(e))) |
38
54405de302d0
Attempt to delete stale events. Still needs a check for already deleted
Matti Hamalainen <ccr@tnsp.org>
parents:
37
diff
changeset
|
677 |
85
da09a3b1edaa
Count and report number of purged events.
Matti Hamalainen <ccr@tnsp.org>
parents:
84
diff
changeset
|
678 gcm_debug(3, "{0} events purged.".format(evn_purged)) |
da09a3b1edaa
Count and report number of purged events.
Matti Hamalainen <ccr@tnsp.org>
parents:
84
diff
changeset
|
679 |
da09a3b1edaa
Count and report number of purged events.
Matti Hamalainen <ccr@tnsp.org>
parents:
84
diff
changeset
|
680 |
75 | 681 ## |
682 ## Finally, update the calendar name with timestamp | |
683 ## | |
73
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
684 t_time = time.localtime() |
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
685 t_str = time.strftime("%d.%m.%Y %H:%M", t_time) |
77
e5e7b6e9bd44
Implement debug levels in the main code.
Matti Hamalainen <ccr@tnsp.org>
parents:
76
diff
changeset
|
686 gcm_debug(3, u"Updating target calendar name timestamp {0}".format(t_str)) |
73
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
687 |
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
688 try: |
98
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
689 dst_calendar["summary"] = cfg.dst_name.format(t_str) |
fe3bfabf0b5f
Rename settings dest_* and source_* to dst_* and src_* respectively.
Matti Hamalainen <ccr@tnsp.org>
parents:
97
diff
changeset
|
690 new_calendar = service.calendars().update(calendarId=cfg.dst_id, body=dst_calendar).execute() |
73
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
691 except Exception as e: |
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
692 gcm_fatal(u"Failed to update target calendar:\n{0}\n\nERROR: {1}\n".format(dst_calendar, str(e))) |
b3f8621f1a25
Change how the target calendar settings work a bit. Also implement display
Matti Hamalainen <ccr@tnsp.org>
parents:
72
diff
changeset
|
693 |
27 | 694 |
87
c3f8af496f46
Add "execution time elapsed" metering.
Matti Hamalainen <ccr@tnsp.org>
parents:
86
diff
changeset
|
695 gcm_bench_end = time.time() |
c3f8af496f46
Add "execution time elapsed" metering.
Matti Hamalainen <ccr@tnsp.org>
parents:
86
diff
changeset
|
696 gcm_bench_elapsed = gcm_bench_end - gcm_bench_start |
c3f8af496f46
Add "execution time elapsed" metering.
Matti Hamalainen <ccr@tnsp.org>
parents:
86
diff
changeset
|
697 |
c3f8af496f46
Add "execution time elapsed" metering.
Matti Hamalainen <ccr@tnsp.org>
parents:
86
diff
changeset
|
698 gcm_debug(3, u"Finished. {0} seconds elapsed.".format(gcm_bench_elapsed)) |