12 changed files with 324 additions and 296 deletions
-
14gapi/__init__.py
-
0gapi/apis/__init__.py
-
6gapi/apis/base_api.py
-
5gapi/apis/calendar_api/__init__.py
-
98gapi/apis/calendar_api/calendar_api.py
-
136gapi/apis/calendar_api/models.py
-
22gapi/apis/calendar_api/utils.py
-
0gapi/apis/drive_api/__init__.py
-
0gapi/apis/drive_api/drive_api.py
-
279gapi/calendar_api.py
-
14gapi/config.py
-
46tests/event_class.py
@ -0,0 +1,14 @@ |
|||
import os |
|||
|
|||
import appdirs |
|||
|
|||
app_dirs = appdirs.AppDirs("gapi") |
|||
config_dir = app_dirs.user_config_dir |
|||
cache_dir = app_dirs.user_cache_dir |
|||
log_dir = app_dirs.user_log_dir |
|||
state_dir = app_dirs.user_state_dir |
|||
|
|||
|
|||
for dir in (config_dir, cache_dir, log_dir, state_dir): |
|||
if not os.path.exists(dir): |
|||
os.makedirs(dir) |
|||
@ -0,0 +1,5 @@ |
|||
import os |
|||
|
|||
|
|||
from .models import Event |
|||
from .calendar_api import calendar_api |
|||
@ -0,0 +1,98 @@ |
|||
import os |
|||
|
|||
from googleapiclient.errors import HttpError |
|||
|
|||
import gapi |
|||
from gapi.apis.base_api import API |
|||
|
|||
APPLICATION_NAME = "Google Calendar API Python" |
|||
|
|||
|
|||
class calendar_api(API): |
|||
def __init__( |
|||
self, |
|||
app_name, |
|||
client_secret_file=os.path.join(gapi.state_dir, "client_secret.json"), |
|||
credentials_dir=gapi.state_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() |
|||
@ -0,0 +1,136 @@ |
|||
from enum import IntEnum |
|||
import datetime |
|||
import re |
|||
|
|||
from dateutil import rrule |
|||
|
|||
from .calendar_api import calendar_api |
|||
from .utils import to_dateTime, from_dateTime |
|||
|
|||
|
|||
class weekdays(IntEnum): |
|||
SUN = 6 |
|||
MON = 0 |
|||
TUE = 1 |
|||
WED = 2 |
|||
THU = 3 |
|||
FRI = 4 |
|||
SAT = 5 |
|||
|
|||
|
|||
class Calendar: |
|||
"""Model for representing a Google calendar""" |
|||
|
|||
pass |
|||
|
|||
|
|||
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.recurrence = recurrence |
|||
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): |
|||
if not until.tzinfo: |
|||
until = until.astimezone() |
|||
ret = rrule.rrule( |
|||
freq=rrule.WEEKLY, |
|||
dtstart=self.start, |
|||
wkst=weekdays.SUN, |
|||
until=until, |
|||
byweekday=days, |
|||
) |
|||
ret_str = str(ret).split("\n")[-1] |
|||
ret_str = re.sub(r"(UNTIL=[^;]+)", r"\1Z", ret_str) |
|||
if self.recurrence is None: |
|||
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()) |
|||
self.id = event_id |
|||
return event_id |
|||
|
|||
def delete(self, api: calendar_api): |
|||
pass |
|||
@ -0,0 +1,22 @@ |
|||
import datetime |
|||
|
|||
import tzlocal |
|||
from dateutil import tz |
|||
from dateutil.parser import parse as date_parse |
|||
|
|||
|
|||
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 |
|||
@ -1,279 +0,0 @@ |
|||
import os |
|||
from dateutil import rrule, tz |
|||
from oauth2client import tools |
|||
from googleapiclient.errors import HttpError |
|||
from dateutil.parser import parse as date_parse |
|||
import re |
|||
import tzlocal |
|||
import argparse |
|||
import datetime |
|||
|
|||
if __name__ == "__main__": |
|||
from api import API |
|||
else: |
|||
from gapi.api import API, config |
|||
|
|||
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() |
|||
APPLICATION_NAME = "Google Calendar API Python" |
|||
|
|||
SUN = 6 |
|||
MON = 0 |
|||
TUE = 1 |
|||
WED = 2 |
|||
THU = 3 |
|||
FRI = 4 |
|||
SAT = 5 |
|||
|
|||
|
|||
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=os.path.join(config.state_dir, "client_secret.json"), |
|||
credentials_dir=config.state_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.recurrence = recurrence |
|||
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): |
|||
if not until.tzinfo: |
|||
until = until.astimezone() |
|||
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) |
|||
if self.recurrence is None: |
|||
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") |
|||
until = datetime.datetime.today() + datetime.timedelta(days=20) |
|||
e2.add_weekly_recurrence(until, MON, TUE) |
|||
# e2.upload(my_api, cal_id) |
|||
@ -1,14 +0,0 @@ |
|||
import os |
|||
|
|||
import appdirs |
|||
|
|||
app_dirs = appdirs.AppDirs("gapi") |
|||
config_dir = app_dirs.user_config_dir |
|||
cache_dir = app_dirs.user_cache_dir |
|||
log_dir = app_dirs.user_log_dir |
|||
state_dir = app_dirs.user_state_dir |
|||
|
|||
|
|||
for dir in (config_dir, cache_dir, log_dir, state_dir): |
|||
if not os.path.exists(dir): |
|||
os.makedirs(dir) |
|||
@ -0,0 +1,46 @@ |
|||
from dateutil.parser import parse as date_parse |
|||
|
|||
from gapi.apis.calendar_api.models import weekdays, Event |
|||
from gapi.apis.calendar_api import calendar_api |
|||
import datetime |
|||
|
|||
if __name__ == "__main__": |
|||
|
|||
MY_API = calendar_api("fuck google") |
|||
# ~~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", |
|||
) |
|||
CAL_ID = "raphael.roberts48@gmail.com" |
|||
event = Event.from_json(example) |
|||
id = event.upload(MY_API, CAL_ID) |
|||
print(id) |
|||
e2 = Event.from_id(MY_API, CAL_ID, id) |
|||
until = datetime.datetime.today() + datetime.timedelta(days=20) |
|||
e2.add_weekly_recurrence(until, weekdays.MON, weekdays.TUE) |
|||
e2.upload(MY_API, CAL_ID) |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue