|
|
|
@ -1,14 +1,15 @@ |
|
|
|
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 |
|
|
|
from api import API, HttpError |
|
|
|
else: |
|
|
|
from gapi.api import API |
|
|
|
from gapi.api import API, HttpError |
|
|
|
|
|
|
|
import datetime |
|
|
|
import argparse |
|
|
|
import tzlocal |
|
|
|
from dateutil import rrule,tz |
|
|
|
from dateutil.parser import parse as date_parse |
|
|
|
from oauth2client import tools |
|
|
|
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() |
|
|
|
APPLICATION_NAME = 'Google Calendar API Python' |
|
|
|
|
|
|
|
@ -20,107 +21,41 @@ THU = 4 |
|
|
|
FRI = 5 |
|
|
|
SAT = 6 |
|
|
|
|
|
|
|
|
|
|
|
def to_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, |
|
|
|
} |
|
|
|
"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 |
|
|
|
|
|
|
|
#~~BODY EXAMPLE~~# |
|
|
|
|
|
|
|
class event: |
|
|
|
def __init__(self, |
|
|
|
start, |
|
|
|
end, |
|
|
|
name, |
|
|
|
description = None, |
|
|
|
recurrence = None, |
|
|
|
location = None, |
|
|
|
id = None |
|
|
|
): |
|
|
|
self.start = start |
|
|
|
self.end = end |
|
|
|
self.summary = name |
|
|
|
self.description = description |
|
|
|
self.location = location |
|
|
|
self.id = id |
|
|
|
self.reminders = {'useDefault':True} |
|
|
|
|
|
|
|
def add_reminder(self,until,method = 'popup'): |
|
|
|
self.recurrence['useDefault'] = False |
|
|
|
if isinstance(until,datetime.timedelta): |
|
|
|
minutes_until = until.days * 24 * 60 |
|
|
|
minutes_until += until.seconds//60 |
|
|
|
else: |
|
|
|
minutes_until = until |
|
|
|
self.recurrence['overrides'] = { |
|
|
|
'method':minutes_until, |
|
|
|
'method':method, |
|
|
|
} |
|
|
|
|
|
|
|
def add_weekly_recurrence(self,until,*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', 'summary') |
|
|
|
ret = { |
|
|
|
'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','unamed') |
|
|
|
args['start'] = from_dateTime(body['start']) |
|
|
|
args['end'] = from_dateTime(body['end']) |
|
|
|
instance = cls(**args) |
|
|
|
keys = ('attendees', 'description', 'location', 'recurrence', 'reminders', 'summary') |
|
|
|
for key in keys: |
|
|
|
try: |
|
|
|
args[key] = body[key] |
|
|
|
except KeyError: |
|
|
|
pass |
|
|
|
|
|
|
|
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) |
|
|
|
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): |
|
|
|
|
|
|
|
@ -129,23 +64,27 @@ class calendar_api(API): |
|
|
|
except KeyError: |
|
|
|
pass |
|
|
|
service = self.service |
|
|
|
event = service.events().insert(calendarId=calendar_id, body=body).execute() |
|
|
|
event_service = service.events() |
|
|
|
event_service.insert(calendarId=calendar_id, body=body).execute() |
|
|
|
return event['id'] |
|
|
|
|
|
|
|
def update_event(self,calendar_id, event_id, body): |
|
|
|
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 = service.events().get(calendarId=calendar_id, eventId=event_id).execute() |
|
|
|
event = event_service.get( |
|
|
|
calendarId=calendar_id, eventId=event_id).execute() |
|
|
|
except HttpError as e: |
|
|
|
if e.resp.status==404: |
|
|
|
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() |
|
|
|
updated_event = service.events().update( |
|
|
|
calendarId=calendar_id, eventId=event['id'], body=body).execute() |
|
|
|
return updated_event["id"] |
|
|
|
|
|
|
|
def get_calendars(self): |
|
|
|
@ -154,38 +93,156 @@ class calendar_api(API): |
|
|
|
first = True |
|
|
|
while page_token or first: |
|
|
|
first = False |
|
|
|
calendar_list = self.service.calendarList().list(pageToken=page_token).execute() |
|
|
|
cl += list(calendar_list_entry for calendar_list_entry in calendar_list['items']) |
|
|
|
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,id): |
|
|
|
def get_events(self, cal_id): |
|
|
|
service = self.service |
|
|
|
try: |
|
|
|
id = self.ids[id] |
|
|
|
cal_id = self.ids[cal_id] |
|
|
|
except KeyError: |
|
|
|
pass |
|
|
|
page_token = None |
|
|
|
ret = [] |
|
|
|
while True: |
|
|
|
events = service.events().list(calendarId=id, pageToken=page_token).execute() |
|
|
|
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(cal_id, event_id).execute() |
|
|
|
|
|
|
|
|
|
|
|
class event: |
|
|
|
"""Model for a calendar event that can be uploaded""" |
|
|
|
|
|
|
|
def __init__(self, api, |
|
|
|
start, |
|
|
|
end, |
|
|
|
name, |
|
|
|
description=None, |
|
|
|
recurrence=None, |
|
|
|
location=None, |
|
|
|
id=None |
|
|
|
): |
|
|
|
self.start = start |
|
|
|
self.end = end |
|
|
|
self.summary = name |
|
|
|
self.description = description |
|
|
|
self.location = location |
|
|
|
self.id = id |
|
|
|
self.reminders = {'useDefault': True} |
|
|
|
|
|
|
|
def add_reminder(self, until, method='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({ |
|
|
|
'minuutes': minutes_until, |
|
|
|
'method': method, |
|
|
|
}) |
|
|
|
|
|
|
|
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', 'summary') |
|
|
|
ret = { |
|
|
|
'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']) |
|
|
|
instance = cls(**args) |
|
|
|
keys = ('attendees', 'description', 'location', |
|
|
|
'recurrence', 'reminders', 'summary') |
|
|
|
for key in keys: |
|
|
|
try: |
|
|
|
args[key] = body[key] |
|
|
|
except KeyError: |
|
|
|
pass |
|
|
|
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: |
|
|
|
api.update_event(calendar_id, self.id, self.to_json) |
|
|
|
else: |
|
|
|
api.create_event(calendar_id, self.to_json) |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
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.from_json(example) |
|
|
|
# ~~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.from_json(example) |