from __future__ import print_function import datetime import dateutil.parser import pickle import json import os.path from googleapiclient.discovery import build from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request # If modifying these scopes, delete the file token.pickle. SCOPES = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/calendar.readonly", "openid"] API_SERVICE_NAME = 'calendar' API_VERSION = 'v3' class Event: def __init__(self, name_, start_, end_): self.name = name_ self.eventColorId = 0 self.calendarColorId = 0 self.naturalColorId = 0 self.start = start_ self.end = end_ self.colorHex = '#adfff5' if self.start == None or self.end == None : self.allDay = True else: self.allDay = False def startDateTime(self): if self.allDay: return None return self.jsonFromDT(self.start) def stopDateTime(self): if self.allDay: return None return self.jsonFromDT(self.end) def jsonFromDT(self, string): sdt = dateutil.parser.parse(string) data = { 'date': { 'year': sdt.year, 'month': sdt.month, 'day': sdt.day }, 'time': { 'hour': sdt.hour, 'minute': sdt.minute, 'second': sdt.second } } return data def print(self): if self.allDay: print(self.name + "All Day") else: print(self.name + ": " + self.naturalColorId) class Calendar: def __init__(self, summary_, calendarId_, color_): self.summary = summary_ self.calendarId = calendarId_ self.color = color_ def calendarCredentials(token = None): creds = None # The file token.pickle stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. if token == None: if os.path.exists('token.pickle'): with open('token.pickle', 'rb') as token: token.seek(0) creds = pickle.load(token) else: creds = pickle.load(token) # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( 'credentials.json', SCOPES) creds = flow.run_local_server(port=1234) # Save the credentials for the next run with open('token.pickle', 'wb') as token: pickle.dump(creds, token) service = build('calendar', 'v3', credentials=creds) return service def getCalendarColors(service): colors = service.colors().get().execute() return colors def getCalendars(service): page_token = None calendars = [] while True: calendar_list = service.calendarList().list(pageToken=page_token).execute() for calendar in calendar_list['items']: calendars.append(Calendar(calendar['summary'], calendar['id'], calendar['colorId'])) page_token = calendar_list.get('nextPageToken') if not page_token: break return calendars def purgeCalendars(calendars, visibleCalendars): purged = [] for calendar in calendars: if calendar.calendarId in visibleCalendars: purged.append(calendar) return purged def getCalendarEvents(service, visibleCalendars, startDate, endDate): calendars = getCalendars(service) calendars = purgeCalendars(calendars, visibleCalendars) all_events = [] for calendar in calendars: events_result = service.events().list(calendarId=calendar.calendarId, timeMin=startDate, timeMax=endDate, maxResults=10, singleEvents=True, orderBy='startTime').execute() for event in events_result.get('items', []): # create simple event name = event.get('summary', '(no Title)') start = event['start'].get('dateTime') end = event['end'].get('dateTime') newEvent = Event(name, start, end) # handle weird colors from google color = event.get('colorId') if color == None: newEvent.calendarColorId = calendar.color else: newEvent.eventColorId = color all_events.append(newEvent) return all_events def toCalendarColorId(colormap, colorId): for remap in colormap['eventRemap']: if remap['from'] == colorId: return remap['to'] print(f"failed with {colorId}") return colorId def toNaturalColor(colormap, orgColor): for remap in colormap['colors']: if remap['api'] == orgColor: return remap['natural'] print(f"failed with {orgColor}") return orgColor def colorizeEvents(allEvents, service): COLORS_FILE = os.path.join(os.path.dirname(__file__), 'colors.json') with open(COLORS_FILE) as f: colormap = json.load(f) colors = getCalendarColors(service) for event in allEvents: event.colorHex = forEventGetColor(event, colormap, colors) # this function is only used once for calendar generation stuff def fromColorIdGetColor(color, colormap, colors): return toNaturalColor(colormap, colors['calendar'][color]['background']) def forEventGetColor(event, colormap, colors): if event.calendarColorId != 0: bg = colors['calendar'][event.calendarColorId]['background'] else: calColorId = toCalendarColorId(colormap, event.eventColorId) bg = colors['calendar'][calColorId]['background'] return toNaturalColor(colormap, bg) def toJson(events): data = {} data['kind'] = 'calendar#events' data['events'] = [] for event in events: if event.allDay: continue data['events'].append({ 'name': event.name, 'isAllDay': event.allDay, 'color': event.colorHex, 'startDateTime': event.startDateTime(), 'stopDateTime': event.stopDateTime() }) return data def printColors(colors): for i in range(1, 25): col = colors['event'][str(i)]['background'] print(f"{i}: {col}") def main(): generateJsonFromCalendarEntries() def getCalendarList(credentials = None): # create service if credentials == None: credentials = calendarCredentials() service = build(API_SERVICE_NAME, API_VERSION, credentials=credentials) calendars = getCalendars(service) COLORS_FILE = os.path.join(os.path.dirname(__file__), 'colors.json') with open(COLORS_FILE) as f: colormap = json.load(f) colors = getCalendarColors(service) for calendar in calendars: calendar.color = fromColorIdGetColor(calendar.color, colormap, colors) return calendars def generateJsonFromCalendarEntries(visibleCalendars, credentials = None): # create service if credentials == None: credentials = calendarCredentials() service = build(API_SERVICE_NAME, API_VERSION, credentials=credentials) # define today and tomorrow now = datetime.datetime.now(datetime.timezone.utc).astimezone() today = now.replace(hour=0, minute=0, second = 0).isoformat() tomorrow = now.replace(hour=23, minute=59, second=59).isoformat() # + '+01:00' twodaysfromnow = datetime.datetime.today() + datetime.timedelta(days=2) twodaysfromnow = twodaysfromnow.replace(hour=23, minute=59, second=59).astimezone().isoformat() print(tomorrow, flush=True) print('----') print(twodaysfromnow, flush=True) allEvents = getCalendarEvents(service, visibleCalendars, today, twodaysfromnow) colorizeEvents(allEvents, service) # if not events: # print('No upcoming events found.') return toJson(allEvents) if __name__ == '__main__': main()