#TODO: All Google/iCloud Calendar syncing logic here import google_auth_oauthlib.flow from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from googleapiclient.discovery import build from googleapiclient.errors import HttpError import os import json import datetime from .database import TodoDatabase data = TodoDatabase() from datetime import datetime, time, timedelta import pytz from zoneinfo import ZoneInfo from datetime import timezone def midnight_UTC(offset): # Construct a timezone object tz = pytz.timezone('America/Toronto') # Work out today/now as a timezone-aware datetime today = datetime.now(tz) # Adjust by the offset. Note that that adding 1 day might actually move us 23 or 25 # hours into the future, depending on daylight savings. This works because the {today} # variable is timezone aware target_day = today + timedelta(days=1) * offset # Discard hours, minutes, seconds and microseconds midnight_aware = tz.localize( datetime.combine(target_day, time(0, 0, 0, 0)), is_dst=None) # Convert to UTC midnight_UTC = midnight_aware.astimezone(pytz.utc) return midnight_UTC class SyncData(): def __init__(self): self.SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] self.creds = None self.get_new_creds() self.get_calendar_events() def get_new_creds(self, code=""): """Returns True if successfully obtains credentials, otherwise throws errors according to functions called, unless code='' in which case if no saved credentials are found, returns False """ if os.path.exists('token.json'): self.creds = Credentials.from_authorized_user_file('token.json', self.SCOPES) # else: # flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( # 'client_secret.json', # scopes=['https://www.googleapis.com/auth/calendar.readonly'], # redirect_uri="postmessage" # # state=state # ) # creds = flow.fetch_token(code=googleCode.code) # print(creds) # with open("token.json", 'w') as token: # token.write(creds.to_json()) if not self.creds or not self.creds.valid: if self.creds and self.creds.expired and self.creds.refresh_token: self.creds.refresh(Request()) else: if code == "": return False # flow = InstalledAppFlow.from_client_secrets_file( # 'credentials.json', SCOPES) # creds = flow.run_local_server(port=0) flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( 'client_secret.json', scopes=self.SCOPES, redirect_uri="postmessage" # state=state ) flow.fetch_token(code=code) self.creds = flow.credentials # print(creds) # Save the credentials for the next run with open('token.json', 'w') as token: token.write(self.creds.to_json()) # json.dump(token, creds) return True def get_calendar_events(self, day=None): self.get_new_creds() # now = datetime.now() # if day: # now = datetime.datetime.strptime(day, "%Y-%m-%d") # else: # now = datetime.datetime.now() # day_start = now.replace(hour=0, minute=0, second=0, microsecond=0) # day_end = day_start + datetime.timedelta(hours=24) # day_start = day_start.isoformat() + "Z" # day_end = day_end.isoformat() + "Z" if day: pass else: # day_start = midnight_UTC(0).isoformat() + "Z" # day_end = midnight_UTC(1).isoformat() + "Z" day_start = datetime.combine(datetime.now(tz=ZoneInfo("America/Toronto")).date(), time(0, 0), tzinfo=ZoneInfo("America/Toronto")) day_end = day_start + timedelta(hours=23,minutes=59) # day_start_for = day_start.astimezone(ZoneInfo("UTC")).isoformat() + "Z" # day_end_for = day_end.astimezone(ZoneInfo("UTC")).isoformat() + "Z" day_start_for = day_start.astimezone(ZoneInfo("UTC")).strftime('%Y-%m-%dT%H:%M:%S.%fZ') day_end_for = day_end.astimezone(ZoneInfo("UTC")).strftime('%Y-%m-%dT%H:%M:%S.%fZ') # print(day_start.tzname()) print(day_start) print(day_end) print(day_start.astimezone(ZoneInfo("UTC")).strftime('%Y-%m-%dT%H:%M:%S.%fZ')) print(day_end.astimezone(ZoneInfo("UTC")).strftime('%Y-%m-%dT%H:%M:%S.%fZ')) print(datetime.utcnow().isoformat() + 'Z') try: service = build('calendar', 'v3', credentials=self.creds) # Call the Calendar API # now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time print('Getting all upcoming events for time range: ' + day_start_for + " to " + day_end_for) events_result = service.events().list(calendarId='primary', timeMin=day_start_for, timeMax=day_end_for, singleEvents=True, orderBy='startTime').execute() events = events_result.get('items', []) if not events: print('No upcoming events found.') return # Prints the start and name of the next 10 events # event format: {'kind': 'calendar#event', 'etag': '"3386046190326000"', 'id': '0migcv6t729ph1kirhiauej3eh', 'status': 'confirmed', 'htmlLink': 'https://www.google.com/calendar/event?eid=MG1pZ2N2NnQ3MjlwaDFraXJoaWF1ZWozZWggYmFtLmltLnNhbUBt', 'created': '2023-08-26T04:11:35.000Z', 'updated': '2023-08-26T04:11:35.163Z', 'summary': 'test', 'creator': {'email': 'bam.im.sam@gmail.com', 'self': True}, 'organizer': {'email': 'bam.im.sam@gmail.com', 'self': True}, 'start': {'dateTime': '2023-08-27T14:30:00-04:00', 'timeZone': 'America/Toronto'}, 'end': {'dateTime': '2023-08-27T15:30:00-04:00', 'timeZone': 'America/Toronto'}, 'iCalUID': '0migcv6t729ph1kirhiauej3eh@google.com', 'sequence': 0, 'reminders': {'useDefault': True}, 'eventType': 'default'} for event in events: print(event) start = event['start'].get('dateTime', event['start'].get('date')) end = event['end'].get('dateTime', event['end'].get('date')) print(start, "->", end, event['summary']) except HttpError as error: print('An error occurred: %s' % error) #TODO: Obtain calendar data and save to local database, parse into printable content