#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 import schedule from .database import TodoDatabase from .tododatatypes import Todo 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() schedule.every(1).day.at("07:55").do(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): if not self.get_new_creds(): return # 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 print(str(len(events)) + " events found") # 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'} formatted_todo_list = [] # print(events) 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(event) # print(start, "->", end, event['summary']) start_parsed = datetime.strptime(start, "%Y-%m-%dT%H:%M:%S%z").strftime("%-I:%M%p") #2023-08-26T15:30:00-04:00 end_parsed = datetime.strptime(end, "%Y-%m-%dT%H:%M:%S%z").strftime("%-I:%M%p") #2023-08-26T15:30:00-04:00 # print("parsed: ", start_parsed, "to", end_parsed, event['summary']) if 'summary' in event: # {"time": start_parsed + " to " + end_parsed, "task": event['summary'], "recurring": False} formatted_todo_list.append(Todo(time=start_parsed + " to " + end_parsed, task=event['summary'], recurring=False)) # formatted_todo_list.append({"time": start_parsed + " to " + end_parsed, "task": event['summary'], "recurring": False}) else: formatted_todo_list.append(Todo(time=start_parsed + " to " + end_parsed, task='No Title', recurring=False)) # formatted_todo_list.append({"time": start_parsed + " to " + end_parsed, "task": 'No Title', "recurring": False}) # print(formatted_todo_list) data.save_todos(day_start.strftime("%Y-%m-%d"), formatted_todo_list) except HttpError as error: print('An error occurred: %s' % error) #TODO: Obtain calendar data and save to local database, parse into printable content