You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
8.6 KiB
271 lines
8.6 KiB
from dateutil import rrule, tz
|
|
from oauth2client import tools
|
|
from dateutil.parser import parse as date_parse
|
|
import re
|
|
import tzlocal
|
|
import argparse
|
|
import datetime
|
|
if __name__ == "__main__":
|
|
from api import API, HttpError
|
|
else:
|
|
from gapi.api import API, HttpError
|
|
|
|
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
|
|
APPLICATION_NAME = 'Google Calendar API Python'
|
|
|
|
SUN = 0
|
|
MON = 1
|
|
TUE = 2
|
|
WED = 3
|
|
THU = 4
|
|
FRI = 5
|
|
SAT = 6
|
|
|
|
|
|
def to_dateTime(datetime: datetime.datetime):
|
|
"""converts a datetime into json format for rest api"""
|
|
if not datetime.tzinfo:
|
|
datetime = datetime.astimezone()
|
|
zone = tzlocal.get_localzone().zone
|
|
datetime = datetime.isoformat(timespec='seconds')
|
|
return {
|
|
"timeZone": zone,
|
|
"dateTime": datetime,
|
|
}
|
|
|
|
|
|
def from_dateTime(dateTime):
|
|
"""converts to a datetime from json format returned by rest api"""
|
|
timezone = tz.gettz(dateTime['timeZone'])
|
|
datetime = date_parse(dateTime['dateTime'])
|
|
datetime.replace(tzinfo=timezone)
|
|
return datetime
|
|
|
|
|
|
class calendar_api(API):
|
|
def __init__(self,
|
|
app_name,
|
|
client_secret_file,
|
|
credentials_dir,
|
|
scopes='https://www.googleapis.com/auth/calendar',
|
|
version='v3',
|
|
):
|
|
super().__init__('calendar', scopes, app_name,
|
|
client_secret_file, credentials_dir, version)
|
|
|
|
self.calendars = self.get_calendars()
|
|
self.ids = dict((calendar['summary'].lower(), calendar['id'])
|
|
for calendar in self.calendars)
|
|
|
|
def create_event(self, calendar_id, body):
|
|
|
|
try:
|
|
calendar_id = self.ids[calendar_id]
|
|
except KeyError:
|
|
pass
|
|
service = self.service
|
|
event_service = service.events()
|
|
event = event_service.insert(
|
|
calendarId=calendar_id, body=body).execute()
|
|
return event['id']
|
|
|
|
def update_event(self, calendar_id, event_id, body):
|
|
try:
|
|
calendar_id = self.ids[calendar_id]
|
|
except KeyError:
|
|
pass
|
|
|
|
service = self.service
|
|
event_service = service.events()
|
|
try:
|
|
event = event_service.get(
|
|
calendarId=calendar_id, eventId=event_id).execute()
|
|
except HttpError as e:
|
|
if e.resp.status == 404:
|
|
return self.create_event(calendar_id, body)
|
|
|
|
updated_event = service.events().update(
|
|
calendarId=calendar_id, eventId=event['id'], body=body).execute()
|
|
return updated_event["id"]
|
|
|
|
def get_calendars(self):
|
|
page_token = None
|
|
cl = []
|
|
first = True
|
|
while page_token or first:
|
|
first = False
|
|
calendar_list_service = self.service.calendarList()
|
|
calendar_list = calendar_list_service.list(
|
|
pageToken=page_token).execute()
|
|
cl += list(
|
|
calendar_entry for calendar_entry in calendar_list['items'])
|
|
page_token = calendar_list.get('nextPageToken')
|
|
return cl
|
|
|
|
def get_events(self, cal_id):
|
|
service = self.service
|
|
try:
|
|
cal_id = self.ids[cal_id]
|
|
except KeyError:
|
|
pass
|
|
page_token = None
|
|
ret = []
|
|
while True:
|
|
event_service = service.events()
|
|
events = event_service.list(
|
|
calendarId=cal_id, pageToken=page_token).execute()
|
|
ret += events['items']
|
|
page_token = events.get('nextPageToken')
|
|
if not page_token:
|
|
break
|
|
return ret
|
|
|
|
def get_event_by_id(self, cal_id, event_id):
|
|
"""Retrieves event from cal_id and event_id"""
|
|
service = self.service
|
|
event_service = service.events()
|
|
return event_service.get(calendarId=cal_id, eventId=event_id).execute()
|
|
|
|
|
|
class Event:
|
|
"""Model for a calendar event that can be uploaded"""
|
|
|
|
def __init__(self,
|
|
start,
|
|
end,
|
|
name,
|
|
description=None,
|
|
recurrence=None,
|
|
reminders=None,
|
|
attendees=None,
|
|
location=None,
|
|
id=None
|
|
):
|
|
self.start = start
|
|
self.attendees = attendees
|
|
self.end = end
|
|
self.name = name
|
|
self.description = description
|
|
self.location = location
|
|
self.id = id
|
|
if reminders is None:
|
|
self.reminders = {'useDefault': True}
|
|
else:
|
|
self.reminders = reminders
|
|
|
|
def add_reminder(self, until, method='popup'):
|
|
"""Add a reminder minutes before an event.
|
|
Use either a notification (popup) or email"""
|
|
assert method in ("email", "popup")
|
|
self.reminders['useDefault'] = False
|
|
if isinstance(until, datetime.timedelta):
|
|
minutes_until = until.days * 24 * 60
|
|
minutes_until += until.seconds//60
|
|
else:
|
|
minutes_until = until
|
|
self.reminders.setdefault('overrides', []).append({
|
|
'method': method,
|
|
'minutes': minutes_until,
|
|
})
|
|
|
|
def add_weekly_recurrence(self, until: datetime.datetime, *days):
|
|
ret = rrule.rrule(freq=rrule.WEEKLY, dtstart=self.start,
|
|
wkst=SUN, until=until, byweekday=days)
|
|
ret_str = str(ret).split('\n')[-1]
|
|
ret_str = re.sub(r'(UNTIL=[^;]+)', r'\1Z', ret_str)
|
|
try:
|
|
self.recurrence
|
|
except AttributeError:
|
|
self.recurrence = []
|
|
self.recurrence.append(ret_str)
|
|
|
|
def to_json(self):
|
|
keys = ('attendees', 'description', 'location',
|
|
'recurrence', 'reminders')
|
|
ret = {
|
|
'summary': self.name,
|
|
'start': to_dateTime(self.start),
|
|
'end': to_dateTime(self.end),
|
|
}
|
|
for key in keys:
|
|
try:
|
|
value = self.__getattribute__(key)
|
|
if value:
|
|
ret[key] = value
|
|
except AttributeError:
|
|
pass
|
|
return ret
|
|
|
|
def add_attendees(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def from_json(cls, body):
|
|
args = {}
|
|
args['name'] = body.get('summary', 'unnamed')
|
|
args['start'] = from_dateTime(body['start'])
|
|
args['end'] = from_dateTime(body['end'])
|
|
keys = ('attendees', 'description', 'location',
|
|
'recurrence', 'reminders', 'id')
|
|
for key in keys:
|
|
try:
|
|
args[key] = body[key]
|
|
except KeyError:
|
|
pass
|
|
instance = cls(**args)
|
|
return instance
|
|
|
|
@classmethod
|
|
def from_id(cls, api: calendar_api, calendar_id, event_id):
|
|
"""creates an event model from specified calendar and event_id"""
|
|
event = api.get_event_by_id(calendar_id, event_id)
|
|
return cls.from_json(event)
|
|
|
|
def upload(self, api: calendar_api, calendar_id):
|
|
"""Upload an event to calendar.
|
|
Either modifies an event in place or creates a new event."""
|
|
if self.id is not None:
|
|
event_id = api.update_event(calendar_id, self.id, self.to_json())
|
|
else:
|
|
event_id = api.create_event(calendar_id, self.to_json())
|
|
return event_id
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import os
|
|
# ~~BODY EXAMPLE~~##~~BODY EXAMPLE~~
|
|
example = {
|
|
'attendees': [{'email': 'lpage@example.com'},
|
|
{'email': 'sbrin@example.com'}],
|
|
'description': "A chance to hear more about Google's\
|
|
developer products.",
|
|
'end': {'dateTime': '2015-05-28T17:00:00-07:00',
|
|
'timeZone': 'America/Los_Angeles'},
|
|
'location': '800 Howard St., San Francisco, CA 94103',
|
|
'recurrence': ['RRULE:FREQ=DAILY;COUNT=2'],
|
|
'reminders': {'overrides': [
|
|
{
|
|
'method': 'email',
|
|
'minutes': 1440
|
|
},
|
|
{
|
|
'method': 'popup',
|
|
'minutes': 10
|
|
}
|
|
],
|
|
'useDefault': False},
|
|
'start': {'dateTime': '2015-05-28T09:00:00-07:00',
|
|
'timeZone': 'America/Los_Angeles'},
|
|
'summary': 'Google I/O 2015'
|
|
}
|
|
e = Event(
|
|
date_parse('march 16, 2019 10:00 am'),
|
|
date_parse('march 16, 2019 3:30 pm'),
|
|
'Hang out with Matt'
|
|
)
|
|
path = r"C:\Users\Raphael\Documents\local_repo\cred"
|
|
my_api = my_api = calendar_api('python', os.path.join(
|
|
path, 'client_secret.json', path), path)
|
|
cal_id = 'raphael.roberts48@gmail.com'
|
|
e2 = Event.from_id(my_api, cal_id, 'qmrsd88ma8ko67ri98d8pbhd7s')
|
|
# e2.upload(my_api, cal_id)
|