diff --git a/README.md b/README.md index 16c023d..3df7dd2 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,41 @@ tool features: - upload files/folders - download files/folders +[google API docs](https://developers.google.com/drive/api/v3/about-sdk) + # prequsities * pipenv * google account * GCP cloud project([details](https://cloud.google.com)) * Enabled Drive API ([details](https://developers.google.com/workspace/guides/create-project)) * OAuth client ID credential (with .json file)([details](https://developers.google.com/workspace/guides/create-credentials)) -* Test Users For Google Oauth Api defined In Google Console([details](https://support.google.com/cloud/answer/10311615?hl=en#publishing-status&zippy=%2Cexternal%2Ctesting)) - +* Test users For Google Oauth Api defined In Google Console([details](https://support.google.com/cloud/answer/10311615?hl=en#publishing-status&zippy=%2Cexternal%2Ctesting)) # Usage -TBD + +Enable pipenv: +``` +cd pyDrive/ +$ pipenv shell +$ (pyDrive) pipenv install +``` + + +Get files from google drive: +``` +$ python3 pyDrive.py pull +``` + +upload a file to google drive: +``` +$ python3 pyDrive.py push +``` + +at first run google will ask you to grant access for application: +``` +$ python3 pyDrive.py push file.txt +Please visit this URL to authorize this application: + +``` +follow the link to authorize the application. +This autorization is a one time event, unless you change/update token.json file diff --git a/googleApi/__init__.py b/googleApi/__init__.py index a39d69a..3ff51d3 100644 --- a/googleApi/__init__.py +++ b/googleApi/__init__.py @@ -1,2 +1,2 @@ from .files import DriveFiles -from .login import api_login +from .login import DriveLogin diff --git a/googleApi/files.py b/googleApi/files.py index a93ac16..9711125 100644 --- a/googleApi/files.py +++ b/googleApi/files.py @@ -1,21 +1,78 @@ +from io import FileIO +from googleapiclient.http import MediaIoBaseDownload +from googleapiclient.http import MediaFileUpload class DriveFiles: - def __init__(self, service) -> None: - self.__service=service - - def list_files(self, page_size): - # Call the Drive v3 API - results = self.__service.files().list( - pageSize=page_size, fields="nextPageToken, files(id, name)").execute() - items = results.get('files', []) - - if not items: - print('No files found.') - return items + def __init__(self, service = None): + self.__service = service + self.__folders_mime="mimeType = 'application/vnd.google-apps.folder'" + self.__root_folder_id=None + + def get_resource_metadata(self, name): + ''' + Description: find specific resource in google drive, by name ( file and folder) + params: resource name + return: dict, keys = ["id", "kind", "name", "parents field"] + ''' + serch_filter = "name="+ "'" + name + "'" + files_service = self.__service.files() + list_service = files_service.list(q = serch_filter, + pageSize=1, + # all possible fields here: + #https://developers.google.com/drive/api/v3/reference/files + fields="nextPageToken, files(kind, id, name, parents)") + search_list =list_service.execute() + if len(search_list['files']) == 0: + return None else: - return items - - def add_files(self, files): - pass - def get_files(self, file_name): - pass \ No newline at end of file + return dict(search_list['files'][0]) + + + def download_file(self, file_name): + ''' + Description: download a file from google drive + params: file_name - name of the file to download + return: None + ''' + + file_meta = self.get_resource_metadata("file_name") + request = self.__service.files().get_media(fileId=file_meta['id']) + fh = FileIO(file_name, 'wb') + downloader = MediaIoBaseDownload(fh, request) + done = False + while done is False: + status, done = downloader.next_chunk() + print (f"Download {int(status.progress() * 100)}.") + + + def upload_file(self, file_name, parent_folder_id): + ''' + Description: upload a file from local to google drive + params: file_name - name of the file to update + parent_folder_id - drive id of folder in which file will be uploaded + return: new_file_id['id'] - new file drive id + ''' + file_metadata = {'name': file_name, + 'parents': [parent_folder_id]} + media = MediaFileUpload(file_name, mimetype='text/plain') + + files_service = self.__service.files() + create_service = files_service.create(body=file_metadata, media_body=media) + create_service.execute() + new_file_id =self.get_resource_metadata(file_name) + return new_file_id['id'] + + + def create_folder(self, folder_name): + # TBD + if self.__service: + folder_metadata = { + 'name': folder_name, + 'mimeType': 'application/vnd.google-apps.folder' + } + files_service = self.__service.files() + create_service = files_service.create(body=folder_metadata,fields='id') + folder_id = create_service.execute() + return folder_id + else: + return "error: no service provided" diff --git a/googleApi/login.py b/googleApi/login.py index 7e4a971..c16e5c3 100644 --- a/googleApi/login.py +++ b/googleApi/login.py @@ -1,51 +1,57 @@ import os.path - from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build -from googleapiclient.errors import HttpError - - -# If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'] - -def validate_creds(creds): - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) +from os import environ + +# TODO: Pass SCOPES and credential tokens as param +class DriveLogin(): + def __init__(self) -> None: + # If modifying these scopes, delete the file token.json. + self.__scopes = ['https://www.googleapis.com/auth/drive', + 'https://www.googleapis.com/auth/drive.readonly', + 'https://www.googleapis.com/auth/drive.metadata'] + # TODO: read token and creds dirs form env variables, validate them + + def validate_creds(self, creds): + ''' + Description: Validate provided credential + params: cred - credential file (json) to validate + return: new_file_id['id'] - new file drive id + ''' + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + 'pyDrive.json', self.__scopes) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open('token.json', 'w') as token: + token.write(creds.to_json()) else: - flow = InstalledAppFlow.from_client_secrets_file( - 'pyDrive.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) - else: - pass - return creds + pass + return creds -# If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'] -def api_login(): - creds = None + def api_login(self): + ''' + Description: log in into google cloud service + params: none + return: service - open api session + ''' + creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - else: - pass - - validated_creds = validate_creds(creds) - - try: - service = build('drive', 'v3', credentials=creds) - except HttpError as error: - service = None - print(f'An error occurred: {error}') - finally: + if os.path.exists('token.json'): + creds = Credentials.from_authorized_user_file('token.json', self.__scopes) + else: + pass + validated_creds = self.validate_creds(creds) + + service = build('drive', 'v3', credentials=validated_creds) return service diff --git a/pyDrive.py b/pyDrive.py index 18a3ce6..571aaf7 100644 --- a/pyDrive.py +++ b/pyDrive.py @@ -1,9 +1,38 @@ -from googleApi import api_login from googleApi import DriveFiles +from googleApi import DriveLogin +from sys import argv +from os import environ +from os import mkdir +from os import path +from os import chdir +from os import getcwd -api_service = api_login() -drive_files = DriveFiles(api_service) -files = drive_files.list_files(10) -for file in files: - print(file) \ No newline at end of file +DOWNLOAD_DIR='downloads' +login = DriveLogin() +api_service = login.api_login() +drive = DriveFiles(api_service) + +param = argv[1] + +if param == "pull": + if not path.exists(DOWNLOAD_DIR): + mkdir(DOWNLOAD_DIR) + else: + pass + + resource_name = argv[2] + base_dir = getcwd() + chdir(DOWNLOAD_DIR) + drive.download_file(resource_name) + chdir(base_dir) + +elif param == "push": + if len(argv) < 4: + print("file or dir empty") + else: + file = argv[2] + dir = argv[3] + folder_meta = drive.get_resource_metadata(dir) + upload_id = drive.upload_file('test.txt', folder_meta['id']) + print(f" uploaded file id: {upload_id} ")