Commit initial code.

This commit is contained in:
Joey Payne 2016-05-24 09:34:53 -06:00
commit 9130ff0ab5
8 changed files with 492 additions and 0 deletions

12
.gitignore vendored
View file

@ -60,3 +60,15 @@ target/
#Ipython Notebook
.ipynb_checkpoints
#Vim
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

0
drivepy/__init__.py Normal file
View file

31
drivepy/auth.py Normal file
View file

@ -0,0 +1,31 @@
from oauth2client.service_account import ServiceAccountCredentials
import json
import drivepy.config as config
from httplib2 import Http
class ServiceAuthentication(object):
"""Service Authorization for server-server OAuth2 support
Fields:
credentials: ServiceAccountCredentials object loaded from the json file
authorization: HTTP authorization object
"""
def __init__(self, auth_file):
"""Creates a ServiceAuthentication object
Args:
auth_file: a json file location that contains Google API credentials
Returns:
An instance of the ServiceAuthentication object
"""
credentials = None
if isinstance(auth_file, file):
auth_dict = json.load(auth_file)
credentials = ServiceAccountCredentials.from_json_keyfile_dict(auth_dict,
scopes=[config.GOOGLE_DRIVE_SCOPE])
else:
credentials = ServiceAccountCredentials.from_json_keyfile_name(auth_file,
scopes=[config.GOOGLE_DRIVE_SCOPE])
self.credentials = credentials
self.authorization = self.credentials.authorize(Http())

114
drivepy/config.py Normal file
View file

@ -0,0 +1,114 @@
import mimetypes
import os
GOOGLE_DRIVE_SCOPE = 'https://www.googleapis.com/auth/drive'
ALL_FILE_FIELDS = 'files,kind,nextPageToken'
ALL_SINGLE_FILE_FIELDS = 'appProperties,capabilities,contentHints,createdTime,description,explicitlyTrashed,fileExtension,folderColorRgb,fullFileExtension,headRevisionId,iconLink,id,imageMediaMetadata,isAppAuthorized,kind,lastModifyingUser,md5Checksum,mimeType,modifiedByMeTime,modifiedTime,name,originalFilename,ownedByMe,owners,parents,permissions,properties,quotaBytesUsed,shared,sharedWithMeTime,sharingUser,size,spaces,starred,thumbnailLink,trashed,version,videoMediaMetadata,viewedByMe,viewedByMeTime,viewersCanCopyContent,webContentLink,webViewLink,writersCanShare'
ALL_PERMISSION_FIELDS = 'allowFileDiscovery,displayName,domain,emailAddress,id,kind,photoLink,role,type'
class PermissionRole(object):
Reader = 'reader'
Writer = 'writer'
Owner = 'owner'
Commenter = 'commenter'
class PermissionType(object):
User = 'user'
Group = 'group'
Domain = 'domain'
Anyone = 'anyone'
class DownloadType(object):
# Documents
HTML = 'text/html'
RichText = 'text/rtf'
OpenOfficeDoc = 'application/vnd.oasis.opendocument.text'
WordDoc = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
# Spreadsheets
CSV = 'text/csv'
Excel = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
OpenOfficeSheet = 'application/x-vnd.oasis.opendocument.spreadsheet'
# Drawings
JPEG = 'image/jpeg'
PNG = 'image/png'
SVG = 'image/svg+xml'
# Presentations
PowerPoint = 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
# Scripts
JSON = 'application/vnd.google-apps.script+json'
# All except Scripts
PDF = 'application/pdf'
# All except scripts and sheets
PlainText = 'text/plain'
class MimeTypes(object):
Audio = 'application/vnd.google-apps.audio'
Document = 'application/vnd.google-apps.document'
Drawing = 'application/vnd.google-apps.drawing'
File = 'application/vnd.google-apps.file'
Folder = 'application/vnd.google-apps.folder'
Form = 'application/vnd.google-apps.form'
FusionTable = 'application/vnd.google-apps.fusiontable'
Map = 'application/vnd.google-apps.map'
Photo = 'application/vnd.google-apps.photo'
Presentation = 'application/vnd.google-apps.presentation'
Script = 'application/vnd.google-apps.script'
Sites = 'application/vnd.google-apps.sites'
Spreadsheet = 'application/vnd.google-apps.spreadsheet'
Unknown = 'application/vnd.google-apps.unknown'
Video = 'application/vnd.google-apps.video'
TypeDict = {}
types = {
Audio: ['mp3', 'wav', 'aac', 'ogg', 'wma'],
Document: ['txt', 'doc', 'docx', 'log', 'odt', 'rtf'],
Folder: [''],
Presentation: ['ppt', 'pptx'],
Spreadsheet: ['csv', 'xls', 'xlsx', 'xlt', 'xml', 'ods'],
Video: ['flv', 'mp4', 'mpg', 'mov', 'mkv', 'avi']
}
for mime, exts in types.items():
for ext in exts:
TypeDict[ext] = mime
DownloadTypeDict = {
Document: DownloadType.WordDoc,
Spreadsheet: DownloadType.Excel,
Drawing: DownloadType.PDF,
Presentation: DownloadType.PowerPoint,
Script: DownloadType.JSON
}
@staticmethod
def get_download_type(mime_type):
"""Tries to guess the download type based on the mime_type."""
return MimeTypes.DownloadTypeDict.get(mime_type, 'text/plain')
@staticmethod
def guess_type(path):
"""Guesses the mimetype of a file extension
Args:
path: the file name or path
Returns:
A mimetype string
"""
ext = os.path.split(path)[-1].replace('.', '')
guess = MimeTypes.TypeDict.get(ext)
if guess is None:
guess = mimetypes.guess_type(path)[0] or MimeTypes.File
return guess

136
drivepy/drive.py Normal file
View file

@ -0,0 +1,136 @@
from googleapiclient.discovery import build
import drivepy.config as config
from drivepy.config import MimeTypes
from pprint import pprint
from drivepy.files import GoogleFile
class GoogleDrive(object):
def __init__(self, authentication):
"""Create an instance of GoogleDrive
Args:
authentication: an authentication object to create the service
(eg: auth.ServiceAccountAuthentication)
Returns:
A GoogleDrive instance
"""
self.authentication = authentication
self.service = build('drive', 'v3', http=self.authentication.authorization)
def list_files(self, **params):
"""List files in a way specified by params
Args:
params: a dict-like object, all available parameters for
the REST API "files" may be specified here
Returns:
A list of files in the drive.
"""
fields = params.pop('fields', config.ALL_FILE_FIELDS)
files = self.service.files().list(fields=fields,
**params).execute().get('files')
return files
def create_file_from_path(self, path, content=None, mime_type=None, permissions=None):
"""Creates a file on google drive from a path
Args:
path: a file or folder path
content: a string with the contents of the new file
mime_type: the type of the file to be created
permissions: a list of dict-like objects that describe permissions
(eg. {'email': 'jap@hotmail.com',
'role': PermissionRole.Writer,
'type': PermissionType.User}
Returns:
A list of the newly created file and it's directories
Raises:
A GoogleFileException if the permissions object is specified and
there is no 'email' key
"""
file_sections = path.split('/')
folders = file_sections[:-1]
file_name = file_sections[-1]
parent_id = None
parent_folders = []
for folder_name in folders:
params = {}
if parent_id:
params['body'] = {'parents': [parent_id]}
google_file = GoogleFile.create(self.service, folder_name,
mime_type=MimeTypes.Folder,
**params)
parent_id = google_file.meta_data['id']
parent_folders.append(google_file)
main_params = {}
if parent_id:
main_params['body'] = {'parents': [parent_id]}
main_file = GoogleFile.create(self.service, file_name,
mime_type=mime_type,
**main_params)
result = parent_folders + [main_file]
if permissions:
for permission in permissions:
email = permission.pop('email', '')
if not email:
raise Exception('There must be an email key in the permissions object!')
for google_file in result:
google_file.give_permission(email, **permission)
return result
def get_file_from_path(self, path):
"""
Parses a file path into a file query and gets
the corresponding file objects associated
Args:
path: a file path from the root of Google Drive
in the form "Folder/SubFolder/file.ext"
Returns:
A list of file objects
"""
file_sections = path.split('/')
folders = file_sections[:-1]
file_name = file_sections[-1]
parent_id = None
for folder in folders:
if parent_id:
folds = self.list_files(q='name = "{}" and "{}" in parents'.format(folder, parent_id),
fields='files/id')
else:
folds = self.list_files(q='name = "{}"'.format(folder), fields='files/id')
if folds:
fold = folds[0]
parent_id = fold['id']
else:
raise Exception('File {} does not exist!'.format(path))
files = self.list_files(q='name = "{}" and "{}" in parents'.format(file_name, parent_id))
result_files = []
for file_meta in files:
result_files.append(GoogleFile(self.service, file_meta))
return result_files

183
drivepy/files.py Normal file
View file

@ -0,0 +1,183 @@
from googleapiclient.http import MediaIoBaseUpload, MediaIoBaseDownload
from drivepy.config import MimeTypes, PermissionType, PermissionRole
import drivepy.config as config
import io
class GoogleFile(object):
"""An object representing a file on GoogleDrive"""
def __init__(self, service, meta_data):
"""Create an instance of a GoogleFile
Args:
service: the api service object
metadata: the metadata for the file
Returns:
An instance of GoogleFile
"""
self.meta_data = meta_data
self.service = service
self.content = ''
@staticmethod
def create(service, name, mime_type=None, content=None, body=None, **params):
"""Creates a new file in GoogleDrive
Args:
service: the API service object
name: the name of the file, including the extension
mime_type: the mimetype of the file. See config.MimeTypes
content: the content string of the file
body: a dict-like object, describes additional request body args
params: any additional parameters to send to the API
Returns:
A GoogleFile object
"""
fields = config.ALL_SINGLE_FILE_FIELDS
body = body or {}
body.update({'name': name})
if mime_type is not None:
body['mimeType'] = mime_type
else:
guess = MimeTypes.guess_type(name)
if guess is not None:
body['mimeType'] = guess
params = {}
if content:
cont = io.BytesIO(content.encode('utf-8'))
media_body = MediaIoBaseUpload(cont,
body['mimeType'],
resumable=True)
params['media_body'] = media_body
meta_data = service.files().create(body=body, fields=fields, **params).execute()
google_file = GoogleFile(service, meta_data)
google_file.content = content
return google_file
def refresh(self):
"""Gets the meta data of the file from Google Drive
Returns:
The meta data of the file
"""
fields = config.ALL_SINGLE_FILE_FIELDS
self.meta_data = self.service.files().get(fileId=self.meta_data['id'], fields=fields).execute()
return self.meta_data
def give_permission(self, email, send_notification=False,
email_message='', fields=None, **params):
"""Gives permission to an email to view a file
Args:
email: a valid Google email address
send_notification: whether to send an email about the share
email_message: the contents of the email
fields: a string with comma separated field names to include
params: a dict-like object of parameters that can be passed to the api call
Returns:
Meta data from the api call
"""
new_params = {'role': PermissionRole.Reader,
'type': PermissionType.User}
new_params.update(params)
new_params['emailAddress'] = email
fields = fields or config.ALL_PERMISSION_FIELDS
file_id = self.meta_data.get('id')
permission_service = self.service.permissions()
email_params = {}
if email_message:
email_params['emailMessage'] = email_message
res = permission_service.create(
fileId=file_id,
sendNotificationEmail=send_notification,
fields=fields,
body=new_params,
**email_params
).execute()
self.refresh()
return res
def download(self, mime_type=None):
"""Download the content of the file from Google Drive
Args:
mime_type: the mime type of the file to download.
see here:
https://developers.google.com/drive/v3/web/manage-downloads#downloading_google_documents
Returns:
The content of the file
"""
if mime_type is None:
download_type = MimeTypes.get_download_type(self.meta_data['mimeType'])
else:
download_type = mime_type
req = self.service.files().export_media(fileId=self.meta_data['id'],
mimeType=download_type)
data = io.BytesIO()
downloader = MediaIoBaseDownload(data, req)
done = False
while not done:
_, done = downloader.next_chunk()
data.seek(0)
self.content = data.read()
return self.content
def delete(self):
"""Delete the file on Google Drive
Returns:
Metadata for the deleted file
"""
return self.service.files().delete(fileId=self.meta_data['id']).execute()
def update(self, meta_data, content=None):
"""Update the current file meta data and contents on Google Drive
Args:
meta_data: A dict-like object, updates the current meta data from
this data
contents: A string that replaces the contents of the current file
on Google Drive
Returns:
Metadata from the file update
"""
self.meta_data.update(meta_data)
file_id = self.meta_data['id']
params = {}
if content:
cont = io.BytesIO(content.encode('utf-8'))
media_body = MediaIoBaseUpload(cont,
self.meta_data['mimeType'],
resumable=True)
params['media_body'] = media_body
self.content = content
res = self.service.files().update(fileId=file_id,
body=self.meta_data,
**params).execute()
return res

0
drivepy/test/__init__.py Normal file
View file

16
setup.py Normal file
View file

@ -0,0 +1,16 @@
from setuptools import setup
setup(
name='PyDrive',
version='0.1.0',
author='Joey Yakimowich-Payne',
author_email='jyapayne@gmail.com',
packages=['drivepy', 'drivepy.test'],
url='https://github.com/jyapayne/DrivePy/',
license='LICENSE',
description='Pythonic Google Drive API Bindings.',
long_description=open('README.md').read(),
install_requires=[
'google-api-python-client >= 1.5.0'
]
)