comparison multimerge.py @ 77:e5e7b6e9bd44

Implement debug levels in the main code.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 13 Jul 2016 12:33:12 +0300
parents a63cc3633adb
children 784cac877428
comparison
equal deleted inserted replaced
76:a63cc3633adb 77:e5e7b6e9bd44
36 "id", "iCalUID", "etag", "sequence", "gcm_cal_id", 36 "id", "iCalUID", "etag", "sequence", "gcm_cal_id",
37 "created", "updated", "htmlLink", "organizer", "creator", 37 "created", "updated", "htmlLink", "organizer", "creator",
38 ] 38 ]
39 39
40 gcm_log_levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"] 40 gcm_log_levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]
41
42
43 def gcm_get_log_level():
44 return gcm_log_levels.index(cfg.logging_level)
41 45
42 46
43 ## Wrapper for print() that does not break when redirecting stdin/out 47 ## Wrapper for print() that does not break when redirecting stdin/out
44 ## because of piped output not having a defined encoding. We default 48 ## because of piped output not having a defined encoding. We default
45 ## to UTF-8 encoding in output here. 49 ## to UTF-8 encoding in output here.
69 gcm_print(u"FATAL: Oh crap, e-mail sending failed: {0}".format(str(e))) 73 gcm_print(u"FATAL: Oh crap, e-mail sending failed: {0}".format(str(e)))
70 sys.exit(1) 74 sys.exit(1)
71 75
72 76
73 ## Debug messages 77 ## Debug messages
74 def gcm_debug(smsg): 78 def gcm_check_debug(level):
75 if cfg.debug: 79 return cfg.debug and gcm_get_log_level() >= level
76 gcm_print(u"DBG: "+ smsg) 80
81 def gcm_debug(level, smsg):
82 if gcm_check_debug(level):
83 gcm_print(u"DBG: {0}".format(smsg))
77 else: 84 else:
78 gcm_msgbuf.append(u"DBG: {0}".format(smsg)) 85 gcm_msgbuf.append(u"DBG: {0}".format(smsg))
79 86
80 87
81 ## Handler for SIGINT signals 88 ## Handler for SIGINT signals
248 def mread(self, cfgparser, sect): 255 def mread(self, cfgparser, sect):
249 for name in self.m_saveable: 256 for name in self.m_saveable:
250 if cfgparser.has_option(sect, name): 257 if cfgparser.has_option(sect, name):
251 value = cfgparser.get(sect, name) 258 value = cfgparser.get(sect, name)
252 self.mset(name, value) 259 self.mset(name, value)
253 gcm_debug(u"{0} -> '{1}' == {2}".format(name, value, self.mget(name))) 260 gcm_debug(4, u"{0} -> '{1}' == {2}".format(name, value, self.mget(name)))
254 261
255 def is_str(self, mvalue): 262 def is_str(self, mvalue):
256 return isinstance(mvalue, basestring) 263 return isinstance(mvalue, basestring)
257 264
258 def is_string(self, mvalue): 265 def is_string(self, mvalue):
364 cfg.mdef("credential_file", True, cfg.is_filename, None, "client_credentials.json") 371 cfg.mdef("credential_file", True, cfg.is_filename, None, "client_credentials.json")
365 372
366 373
367 ## Read, parse and validate configuration file 374 ## Read, parse and validate configuration file
368 if len(sys.argv) > 1: 375 if len(sys.argv) > 1:
369 gcm_debug(u"Reading configuration from '{0}'.".format(sys.argv[1])) 376 gcm_debug(3, u"Reading configuration from '{0}'.".format(sys.argv[1]))
370 try: 377 try:
371 cfgparser = ConfigParser.RawConfigParser() 378 cfgparser = ConfigParser.RawConfigParser()
372 cfgparser.readfp(codecs.open(sys.argv[1], "r", "UTF-8")) 379 cfgparser.readfp(codecs.open(sys.argv[1], "r", "UTF-8"))
373 except Exception as e: 380 except Exception as e:
374 gcm_fatal(u"Failed to read configuration file '{0}': {1}".format(sys.argv[1], str(e))) 381 gcm_fatal(u"Failed to read configuration file '{0}': {1}".format(sys.argv[1], str(e)))
419 http = credentials.authorize(httplib2.Http()) 426 http = credentials.authorize(httplib2.Http())
420 service = discovery.build("calendar", "v3", http=http) 427 service = discovery.build("calendar", "v3", http=http)
421 428
422 429
423 ## Fetch complete calendar list 430 ## Fetch complete calendar list
424 gcm_debug(u"Fetching available calendars ..") 431 gcm_debug(3, u"Fetching available calendars ..")
425 calendars = [] 432 calendars = []
426 cal_token = None 433 cal_token = None
427 while True: 434 while True:
428 # We want everything except deleted and hidden calendars 435 # We want everything except deleted and hidden calendars
429 result = service.calendarList().list( 436 result = service.calendarList().list(
438 break 445 break
439 446
440 if len(calendars) == 0: 447 if len(calendars) == 0:
441 gcm_fatal(u"No calendars found?") 448 gcm_fatal(u"No calendars found?")
442 449
443 gcm_debug(u"{0} calendars total found.".format(len(calendars))) 450 gcm_debug(3, u"{0} calendars total found.".format(len(calendars)))
444 451
445 452
446 ## Filter desired SOURCE calendars based on specified regexp 453 ## Filter desired SOURCE calendars based on specified regexp
447 src_re = re.compile(cfg.source_regex, re.UNICODE) 454 src_re = re.compile(cfg.source_regex, re.UNICODE)
448 dst_re = re.compile(cfg.dest_regex, re.UNICODE) 455 dst_re = re.compile(cfg.dest_regex, re.UNICODE)
463 if mre: 470 if mre:
464 calendar["gcm_title"] = mre.group(cfg.source_regmap[0]) 471 calendar["gcm_title"] = mre.group(cfg.source_regmap[0])
465 calendar["gcm_id"] = mre.group(cfg.source_regmap[1]) 472 calendar["gcm_id"] = mre.group(cfg.source_regmap[1])
466 src_calendars.append(calendar) 473 src_calendars.append(calendar)
467 474
468 gcm_debug(u"{0} source calendars found.".format(len(src_calendars))) 475 gcm_debug(3, u"{0} source calendars found.".format(len(src_calendars)))
469 476
470 477
471 ## Check if we have destination calendar ID 478 ## Check if we have destination calendar ID
472 if not dst_calendar: 479 if not dst_calendar:
473 gcm_fatal(u"Could not find target/destination calendar ID for '"+ cfg.dest_name +"'.") 480 gcm_fatal(u"Could not find target/destination calendar ID for '"+ cfg.dest_name +"'.")
474 else: 481 else:
475 gcm_debug(u"Target calendar '{0}' id {1}.".format(dst_calendar["summary"], dst_calendar["id"])) 482 gcm_debug(3, u"Target calendar '{0}' id {1}.".format(dst_calendar["summary"], dst_calendar["id"]))
476 483
477 484
478 ## Fetch colors 485 ## Fetch colors
479 try: 486 try:
480 colors = service.colors().get().execute() 487 colors = service.colors().get().execute()
481 except Exception as e: 488 except Exception as e:
482 gcm_fatal(u"Failed to fetch calendar color settings:\n\n{0}".format(str(e))) 489 gcm_fatal(u"Failed to fetch calendar color settings:\n\n{0}".format(str(e)))
483 490
484 491
485 ## Now, we fetch and collect events 492 ## Now, we fetch and collect events
486 gcm_debug(u"Fetching calendar events .. ") 493 gcm_debug(3, u"Fetching calendar events .. ")
487 src_events = [] 494 src_events = []
488 for calendar in src_calendars: 495 for calendar in src_calendars:
489 gcm_debug(u"- "+calendar["id"]) 496 gcm_debug(4, u"- "+calendar["id"])
490 try: 497 try:
491 result = service.events().list( 498 result = service.events().list(
492 timeZone="EEST", 499 timeZone="EEST",
493 calendarId=calendar["id"], 500 calendarId=calendar["id"],
494 singleEvents=True, 501 singleEvents=True,
498 except Exception as e: 505 except Exception as e:
499 gcm_fatal(u"Failed to fetch calendar events for {0}:\n\n{1}\n\nERROR: {2}\n".format(calendar["id"], calendar, str(e))) 506 gcm_fatal(u"Failed to fetch calendar events for {0}:\n\n{1}\n\nERROR: {2}\n".format(calendar["id"], calendar, str(e)))
500 507
501 c_found = None 508 c_found = None
502 if "colorId" in calendar and calendar["colorId"] in colors["calendar"]: 509 if "colorId" in calendar and calendar["colorId"] in colors["calendar"]:
503 gcm_debug(u"Calendar color: {0}".format(colors["calendar"][calendar["colorId"]])) 510 gcm_debug(4, u"Calendar color: {0}".format(colors["calendar"][calendar["colorId"]]))
504 c_found = gcm_find_nearest_color(colors["event"], colors["calendar"][calendar["colorId"]], 100) 511 c_found = gcm_find_nearest_color(colors["event"], colors["calendar"][calendar["colorId"]], 100)
505 if c_found: 512 if c_found:
506 gcm_debug(u"Found nearest event color ID: {0}, {1}".format(c_found, colors["event"][c_found])) 513 gcm_debug(4, u"Found nearest event color ID: {0}, {1}".format(c_found, colors["event"][c_found]))
507 else: 514 else:
508 gcm_debug(u"No matching event color found!") 515 gcm_debug(4, u"No matching event color found!")
509 516
510 # Add events, if any, to main list 517 # Add events, if any, to main list
511 events = gcm_generate_ids(result.get("items", []), calendar["id"], "___") 518 events = gcm_generate_ids(result.get("items", []), calendar["id"], "___")
512 if events: 519 if events:
513 for event in events: 520 for event in events:
514 if c_found != None: 521 if c_found != None:
515 event["colorId"] = c_found 522 event["colorId"] = c_found
516 event["summary"] = u"[{1}] {0}".format(event["summary"], calendar["gcm_id"]) 523 event["summary"] = u"[{1}] {0}".format(event["summary"], calendar["gcm_id"])
517 src_events.extend(events) 524 src_events.extend(events)
518 if cfg.debug: 525 if gcm_check_debug(4):
519 gcm_dump_events(events) 526 gcm_dump_events(events)
520 527
521 528
522 ## Get current events 529 ## Get current events
523 gcm_debug(u"Fetching current target calendar events {0}".format(cfg.dest_id)) 530 gcm_debug(3, u"Fetching current target calendar events {0}".format(cfg.dest_id))
524 result = service.events().list( 531 result = service.events().list(
525 calendarId=cfg.dest_id, 532 calendarId=cfg.dest_id,
526 singleEvents=True, 533 singleEvents=True,
527 showDeleted=True, 534 showDeleted=True,
528 ).execute() 535 ).execute()
529 536
530 dst_events = gcm_generate_ids(result.get("items", []), "", "") 537 dst_events = gcm_generate_ids(result.get("items", []), "", "")
531 gcm_debug(u"Found {0} event(s).".format(len(dst_events))) 538 gcm_debug(3, u"Found {0} event(s).".format(len(dst_events)))
532 539
533 540
534 ## Start merging events .. 541 ## Start merging events ..
535 gcm_debug(u"Re-merging events to target calendar ..") 542 gcm_debug(3, u"Re-merging events to target calendar ..")
536 dst_ids = frozenset(map(lambda x: x["gcm_id"], dst_events)) 543 dst_ids = frozenset(map(lambda x: x["gcm_id"], dst_events))
537 src_ids = frozenset(map(lambda x: x["gcm_id"], src_events)) 544 src_ids = frozenset(map(lambda x: x["gcm_id"], src_events))
538 545
539 for event in src_events: 546 for event in src_events:
540 # Does the event exist already in the target? 547 # Does the event exist already in the target?
541 if event["gcm_id"] in dst_ids: 548 if event["gcm_id"] in dst_ids:
542 # Check if event NEEDS updating .. aka compare data 549 # Check if event NEEDS updating .. aka compare data
543 gcm_debug(u"Event {0} : {1} exists, checking ..".format(event["id"], event["gcm_id"])) 550 gcm_debug(4, u"Event {0} : {1} exists, checking ..".format(event["id"], event["gcm_id"]))
544 d_event = gcm_get_event_by_gcm_id(dst_events, event["gcm_id"]) 551 d_event = gcm_get_event_by_gcm_id(dst_events, event["gcm_id"])
545 if not gcm_compare_events(event, d_event): 552 if not gcm_compare_events(event, d_event):
546 # Seems we need to update 553 # Seems we need to update
547 gcm_debug(u"Updating event {0} : {1}..".format(event["id"], event["gcm_id"])) 554 gcm_debug(4, u"Updating event {0} : {1}..".format(event["id"], event["gcm_id"]))
548 try: 555 try:
549 event.pop("sequence", None) 556 event.pop("sequence", None)
550 event.pop("id", None) 557 event.pop("id", None)
551 event["iCalUID"] = event["gcm_id"] 558 event["iCalUID"] = event["gcm_id"]
552 new_event = service.events().update(calendarId=cfg.dest_id, eventId=d_event["id"], body=event).execute() 559 new_event = service.events().update(calendarId=cfg.dest_id, eventId=d_event["id"], body=event).execute()
553 except Exception as e: 560 except Exception as e:
554 gcm_fatal(u"Failed to update event {0}:\n\n{1}\n\nERROR: {2}\n".format(event["gcm_id"], event, str(e))) 561 gcm_fatal(u"Failed to update event {0}:\n\n{1}\n\nERROR: {2}\n".format(event["gcm_id"], event, str(e)))
555 else: 562 else:
556 gcm_debug(u"No need to update event {0} : {1}.".format(event["id"], event["gcm_id"])) 563 gcm_debug(4, u"No need to update event {0} : {1}.".format(event["id"], event["gcm_id"]))
557 else: 564 else:
558 ## Event does not seem to exist. Insert new event. 565 ## Event does not seem to exist. Insert new event.
559 gcm_debug(u"Inserting new event {0}".format(event["gcm_id"])) 566 gcm_debug(4, u"Inserting new event {0}".format(event["gcm_id"]))
560 event.pop("id", None) 567 event.pop("id", None)
561 event["iCalUID"] = event["gcm_id"] # Replace Google generated ID with our own 568 event["iCalUID"] = event["gcm_id"] # Replace Google generated ID with our own
562 try: 569 try:
563 new_event = service.events().insert(calendarId=cfg.dest_id, body=event).execute() 570 new_event = service.events().insert(calendarId=cfg.dest_id, body=event).execute()
564 except Exception as e: 571 except Exception as e:
565 gcm_fatal(u"Failed to insert new event:\n\n{0}\n\nERROR: {1}\n".format(event, str(e))) 572 gcm_fatal(u"Failed to insert new event:\n\n{0}\n\nERROR: {1}\n".format(event, str(e)))
566 573
567 574
568 ## Remove "stale" events 575 ## Remove "stale" events
569 gcm_debug(u"Purging stale events ..") 576 gcm_debug(3, u"Purging stale events ..")
570 for event in dst_events: 577 for event in dst_events:
571 gcm_debug(u"Checking event {0}".format(event["gcm_id"])) 578 gcm_debug(4, u"Checking event {0}".format(event["gcm_id"]))
572 if not event["gcm_id"] in src_ids and event["status"] != u"cancelled": 579 if not event["gcm_id"] in src_ids and event["status"] != u"cancelled":
573 gcm_debug(u"Deleting event {0}".format(event["gcm_id"])) 580 gcm_debug(4, u"Deleting event {0}".format(event["gcm_id"]))
574 try: 581 try:
575 service.events().delete(calendarId=cfg.dest_id, eventId=event["id"]).execute() 582 service.events().delete(calendarId=cfg.dest_id, eventId=event["id"]).execute()
576 except Exception as e: 583 except Exception as e:
577 gcm_fatal(u"Failed to delete stale event:\n{0}\n\nERROR: {1}\n".format(event, str(e))) 584 gcm_fatal(u"Failed to delete stale event:\n{0}\n\nERROR: {1}\n".format(event, str(e)))
578 585
579 ## 586 ##
580 ## Finally, update the calendar name with timestamp 587 ## Finally, update the calendar name with timestamp
581 ## 588 ##
582 t_time = time.localtime() 589 t_time = time.localtime()
583 t_str = time.strftime("%d.%m.%Y %H:%M", t_time) 590 t_str = time.strftime("%d.%m.%Y %H:%M", t_time)
584 gcm_debug(u"Updating target calendar name timestamp {0}".format(t_str)) 591 gcm_debug(3, u"Updating target calendar name timestamp {0}".format(t_str))
585 592
586 try: 593 try:
587 dst_calendar["summary"] = cfg.dest_name.format(t_str) 594 dst_calendar["summary"] = cfg.dest_name.format(t_str)
588 new_calendar = service.calendars().update(calendarId=cfg.dest_id, body=dst_calendar).execute() 595 new_calendar = service.calendars().update(calendarId=cfg.dest_id, body=dst_calendar).execute()
589 except Exception as e: 596 except Exception as e:
590 gcm_fatal(u"Failed to update target calendar:\n{0}\n\nERROR: {1}\n".format(dst_calendar, str(e))) 597 gcm_fatal(u"Failed to update target calendar:\n{0}\n\nERROR: {1}\n".format(dst_calendar, str(e)))
591 598
592 599
593 gcm_debug(u"Finished.") 600 gcm_debug(3, u"Finished.")