Browse Source

Blackened files and using appdirs to store api token / oauth token

master
Raphael Roberts 6 years ago
parent
commit
49aaf5544c
  1. 3
      .gitignore
  2. 26
      gapi/api.py
  3. 196
      gapi/calendar_api.py
  4. 14
      gapi/config.py
  5. 191
      gapi/drive_api.py
  6. 1
      requirements.txt
  7. 9
      test_drive.py

3
.gitignore

@ -58,4 +58,5 @@ docs/_build/
# PyBuilder
target/
/test/
/test/
.dir-locals.el

26
gapi/api.py

@ -1,20 +1,21 @@
import datetime
from googleapiclient.discovery import build
from googleapiclient.errors import *
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
# from oauth2client.service_account import ServiceAccountCredentials
import httplib2
import os
import argparse
from gapi import config
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
HOUR = datetime.timedelta(seconds=60**2)
HOUR = datetime.timedelta(seconds=60 ** 2)
class API:
@ -23,9 +24,9 @@ class API:
service,
scopes,
app_name,
client_secret_file,
credentials_dir,
version='v3',
client_secret_file=os.path.join(config.state_dir, "client_secret.json"),
credentials_dir=config.state_dir,
version="v3",
):
self.service_name = service
self.app_name = app_name
@ -38,20 +39,18 @@ class API:
def get_credentials(self):
credential_path = os.path.join(self.credentials_dir,
'token.json')
credential_path = os.path.join(self.credentials_dir, "token.json")
store = Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(
self.client_secret_file, self.scopes)
flow = client.flow_from_clientsecrets(self.client_secret_file, self.scopes)
flow.user_agent = self.app_name
if flags:
credentials = tools.run_flow(flow, store, flags)
else:
credentials = tools.run(flow, store)
print('Storing credentials to ' + credential_path)
print("Storing credentials to " + credential_path)
return credentials
def build_service(self):
@ -59,8 +58,9 @@ class API:
http = credentials.authorize(httplib2.Http())
service = build(self.service_name, self.version,
http=http, cache_discovery=False)
service = build(
self.service_name, self.version, http=http, cache_discovery=False
)
return service
def _needs_renewal(self):

196
gapi/calendar_api.py

@ -1,17 +1,19 @@
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, HttpError
from api import API
else:
from gapi.api import API, HttpError
from gapi.api import API
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
APPLICATION_NAME = 'Google Calendar API Python'
APPLICATION_NAME = "Google Calendar API Python"
SUN = 6
MON = 0
@ -27,35 +29,35 @@ def to_dateTime(datetime: datetime.datetime):
if not datetime.tzinfo:
datetime = datetime.astimezone()
zone = tzlocal.get_localzone().zone
datetime = datetime.isoformat(timespec='seconds')
return {
"timeZone": zone,
"dateTime": datetime,
}
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'])
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)
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)
self.ids = dict(
(calendar["summary"].lower(), calendar["id"]) for calendar in self.calendars
)
def create_event(self, calendar_id, body):
@ -65,9 +67,8 @@ class calendar_api(API):
pass
service = self.service
event_service = service.events()
event = event_service.insert(
calendarId=calendar_id, body=body).execute()
return event['id']
event = event_service.insert(calendarId=calendar_id, body=body).execute()
return event["id"]
def update_event(self, calendar_id, event_id, body):
try:
@ -79,13 +80,17 @@ class calendar_api(API):
event_service = service.events()
try:
event = event_service.get(
calendarId=calendar_id, eventId=event_id).execute()
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()
updated_event = (
service.events()
.update(calendarId=calendar_id, eventId=event["id"], body=body)
.execute()
)
return updated_event["id"]
def get_calendars(self):
@ -95,11 +100,9 @@ class calendar_api(API):
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')
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):
@ -113,9 +116,10 @@ class calendar_api(API):
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')
calendarId=cal_id, pageToken=page_token
).execute()
ret += events["items"]
page_token = events.get("nextPageToken")
if not page_token:
break
return ret
@ -130,17 +134,18 @@ class calendar_api(API):
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
):
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
@ -150,43 +155,42 @@ class Event:
self.recurrence = recurrence
self.id = id
if reminders is None:
self.reminders = {'useDefault': True}
self.reminders = {"useDefault": True}
else:
self.reminders = reminders
def add_reminder(self, until, method='popup'):
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
self.reminders["useDefault"] = False
if isinstance(until, datetime.timedelta):
minutes_until = until.days * 24 * 60
minutes_until += until.seconds//60
minutes_until += until.seconds // 60
else:
minutes_until = until
self.reminders.setdefault('overrides', []).append({
'method': method,
'minutes': minutes_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)
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')
keys = ("attendees", "description", "location", "recurrence", "reminders")
ret = {
'summary': self.name,
'start': to_dateTime(self.start),
'end': to_dateTime(self.end),
"summary": self.name,
"start": to_dateTime(self.start),
"end": to_dateTime(self.end),
}
for key in keys:
try:
@ -203,11 +207,10 @@ class Event:
@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')
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]
@ -234,41 +237,42 @@ class Event:
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\
"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'
"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'
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')
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)

14
gapi/config.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)

191
gapi/drive_api.py

@ -6,143 +6,147 @@ import re
import os
APPLICATION_NAME = "Google Calendar API Python"
def path_translate(
root,
want,
path,
file
):
l = len(root)+1
def path_translate(root, want, path, file):
l = len(root) + 1
rel = path[l:]
new_rel = rel.replace(os.sep,'/')
return pathjoin(want,new_rel,file)
new_rel = rel.replace(os.sep, "/")
return pathjoin(want, new_rel, file)
def is_folder(element):
return element['mimeType'] == 'application/vnd.google-apps.folder'
return element["mimeType"] == "application/vnd.google-apps.folder"
def split_path(path,rev=False):
s = re.findall(r'(?:^/|[^/\x00]+)',path)
def split_path(path, rev=False):
s = re.findall(r"(?:^/|[^/\x00]+)", path)
if rev:
return s[::-1]
else:
return s
def without(node,*keys):
def without(node, *keys):
node_meta = set(node.keys())
nope = set(keys)
ret = {}
for key in node_meta-nope:
for key in node_meta - nope:
ret[key] = node[key]
return ret
def remote_path_join(path,*paths):
def remote_path_join(path, *paths):
ret = path
for path in paths:
if not ret.endswith('/'):
ret += '/'
if not ret.endswith("/"):
ret += "/"
else:
ret += path
return ret
class drive_file:
def __init__(self,
api,
info,
parent
):
self.id = info.pop('id')
self.name = info.pop('name')
def __init__(self, api, info, parent):
self.id = info.pop("id")
self.name = info.pop("name")
self.api = api
self.info = info
self.parent = parent
self._fullpath = None
@classmethod
def from_id(cls,id,api,parent):
info = api.service.files().get(fileId='root').execute()
return cls(api,info,parent)
def from_id(cls, id, api, parent):
info = api.service.files().get(fileId="root").execute()
return cls(api, info, parent)
@property
def fullpath(self):
if self._fullpath is None:
if self.parent is None:
path = '/'
path = "/"
else:
path = pathjoin(self.parent.fullpath,self.name)
path = pathjoin(self.parent.fullpath, self.name)
self._fullpath = path
return self._fullpath
def __str__(self):
return '{}@{}'.format(type(self).__name__,self.fullpath)
return "{}@{}".format(type(self).__name__, self.fullpath)
def __repr__(self):
return str(self)
class drive_folder(drive_file):
def __init__(self,
api,
info,
parent
):
super().__init__(api,info,parent)
def __init__(self, api, info, parent):
super().__init__(api, info, parent)
self.folders = {}
self.files = {}
def create_folder(self,name):
def create_folder(self, name):
meta = {
'name':name,
'mimeType':'application/vnd.google-apps.folder',
'parents' : [self.id]
"name": name,
"mimeType": "application/vnd.google-apps.folder",
"parents": [self.id],
}
info = self.api.service.files().create(body = meta).execute()
self.folders[name] = drive_folder(self.api,info,self)
info = self.api.service.files().create(body=meta).execute()
self.folders[name] = drive_folder(self.api, info, self)
def refresh(self):
'''finds all files and folders from a parent'''
"""finds all files and folders from a parent"""
parent_id = self.id
child_list = self.__list_children__()
for child in child_list:
name = child['name']
name = child["name"]
if is_folder(child):
self.folders[name] = drive_folder(self.api,child,self)
self.folders[name] = drive_folder(self.api, child, self)
else:
self.files[name] = drive_file(self.api,child,self)
self.files[name] = drive_file(self.api, child, self)
def __list_children__(self):
page_token = None
first = True
while page_token or first:
first = False
response = self.api.service.files().list(q = "'{}' in parents and trashed = false".format(self.id),pageToken=page_token).execute()
page_token = response.get('nextPageToken',None)
for file in response['files']:
response = (
self.api.service.files()
.list(
q="'{}' in parents and trashed = false".format(self.id),
pageToken=page_token,
)
.execute()
)
page_token = response.get("nextPageToken", None)
for file in response["files"]:
yield file
class drive_api(API):
class drive_api(API):
def __init__(
self,
app_name,
client_secret_file,
credentials_dir,
scopes = 'https://www.googleapis.com/auth/drive',
version = 'v3',
):
super().__init__('drive',scopes,app_name,client_secret_file,credentials_dir,version)
self.root = drive_folder.from_id('root',self,None)
def get_file_by_path(self,path,ret_file = True):
'''gets a file or folder by remote path'''
if isinstance(path,str):
self,
app_name,
client_secret_file,
credentials_dir,
scopes="https://www.googleapis.com/auth/drive",
version="v3",
):
super().__init__(
"drive", scopes, app_name, client_secret_file, credentials_dir, version
)
self.root = drive_folder.from_id("root", self, None)
def get_file_by_path(self, path, ret_file=True):
"""gets a file or folder by remote path"""
if isinstance(path, str):
path = split_path(path)
else:
path = path.copy()
parent = self.root
end = path.pop()
if end == '/':
if end == "/":
return self.root
for sub in path:
if sub != '/':
if sub != "/":
try:
if not parent.folders:
parent.refresh()
@ -162,42 +166,42 @@ class drive_api(API):
except KeyError:
raise FileNotFoundError
def mkdirs(self,path):
'''makes directories if they do not exist already'''
if isinstance(path,str):
def mkdirs(self, path):
"""makes directories if they do not exist already"""
if isinstance(path, str):
path = split_path(path)
else:
path = path.copy()
missing = []
for i in range(len(path),0,-1):
for i in range(len(path), 0, -1):
try:
parent = self.get_file_by_path(path[:i],False)
parent = self.get_file_by_path(path[:i], False)
break
except FileNotFoundError:
missing.append(path[i-1])
missing.append(path[i - 1])
while len(missing) > 0:
name = missing.pop()
parent.create_folder(name)
parent = parent.folders[name]
return parent
def upload(self,local_path,remote_path,verbose = False):
if not isinstance(remote_path,str):
def upload(self, local_path, remote_path, verbose=False):
if not isinstance(remote_path, str):
remote_path = pathjoin(*remote_path)
if os.path.isdir(local_path):
for root,dirs,files in os.walk(local_path,topdown = False):
for root, dirs, files in os.walk(local_path, topdown=False):
for file in files:
new_path = path_translate(local_path,remote_path,root,file)
self.__upload_file__(os.path.join(root,file),new_path)
new_path = path_translate(local_path, remote_path, root, file)
self.__upload_file__(os.path.join(root, file), new_path)
if verbose:
print(new_path)
return self.get_file_by_path(remote_path,False)
return self.get_file_by_path(remote_path, False)
else:
return self.__upload_file__(local_path,remote_path)
return self.__upload_file__(local_path, remote_path)
def __upload_file__(self,local_path,remote_path):
'''creates file if it does not exists otherwise update the file'''
if isinstance(remote_path,str):
def __upload_file__(self, local_path, remote_path):
"""creates file if it does not exists otherwise update the file"""
if isinstance(remote_path, str):
remote_path = split_path(remote_path)
else:
remote_path = remote_path.copy()
@ -205,7 +209,9 @@ class drive_api(API):
try:
old_file = self.get_file_by_path(remote_path)
self.service.files().update(fileId=old_file.id,media_body=_upload).execute()
self.service.files().update(
fileId=old_file.id, media_body=_upload
).execute()
return old_file
except FileNotFoundError:
@ -213,19 +219,18 @@ class drive_api(API):
end = path.pop()
parent = self.mkdirs(path)
parent_id = parent.id
meta = {
'name':end,
'parents':[parent_id]
}
new_f_meta = self.service.files().create(
body = meta,
media_body = _upload
).execute()
new_f_meta = without(new_f_meta,'kind')
new_f = drive_file(self,new_f_meta,parent)
meta = {"name": end, "parents": [parent_id]}
new_f_meta = (
self.service.files().create(body=meta, media_body=_upload).execute()
)
new_f_meta = without(new_f_meta, "kind")
new_f = drive_file(self, new_f_meta, parent)
parent.files[end] = new_f
return new_f
if __name__ == "__main__":
my_api = drive_api(APPLICATION_NAME,r'..\test\drive\client_secret.json',r'..\test\drive')
my_api = drive_api(
APPLICATION_NAME, r"..\test\drive\client_secret.json", r"..\test\drive"
)
service = my_api.service

1
requirements.txt

@ -2,3 +2,4 @@ google-api-python-client
python-dateutil
oauth2client
tzlocal
appdirs

9
test_drive.py

@ -1,8 +1,9 @@
import os
import gapi
from gapi.drive_api import drive_api,APPLICATION_NAME
my_api = drive_api(APPLICATION_NAME,r'test\drive\client_secret.json',r'test\drive')
t1 = my_api.get_file_by_path('/Python/Lib/site-packages',False)
from gapi.drive_api import drive_api, APPLICATION_NAME
my_api = drive_api(APPLICATION_NAME, r"test\drive\client_secret.json", r"test\drive")
t1 = my_api.get_file_by_path("/Python/Lib/site-packages", False)
# t1.refresh()
# t2 = my_api.mkdirs('/test/t1/this/is/a/path/why/isnt/this/working')
# t3 = my_api.upload(os.getcwd(),'/test/t3')
# t3 = my_api.upload(os.getcwd(),'/test/t3')
Loading…
Cancel
Save