Module mongogbackup.gdrive

Classes

class FileDeletionError (file_name: str, file_id: str, message: str)

Raised when there is an error during file deletion.

Expand source code
class FileDeletionError(Exception):
    """Raised when there is an error during file deletion."""
    def __init__(self, file_name: str, file_id: str, message: str):
        self.file_name = file_name
        self.file_id = file_id
        self.message = message
    def __str__(self):
        return f"Error deleting file {self.file_name} (ID: {self.file_id}): {self.message}"

Ancestors

  • builtins.Exception
  • builtins.BaseException
class FileQueryError (message: str)

Raised when there is an error querying files on Google Drive.

Expand source code
class FileQueryError(Exception):
    """Raised when there is an error querying files on Google Drive."""
    def __init__(self, message: str):
        self.message = message
    def __str__(self):
        return f"File query error: {self.message}"

Ancestors

  • builtins.Exception
  • builtins.BaseException
class FileUploadError (file_name: str, message: str)

Raised when there is an error during file upload.

Expand source code
class FileUploadError(Exception):
    """Raised when there is an error during file upload."""
    def __init__(self, file_name: str, message: str):
        self.file_name = file_name
        self.message = message
    def __str__(self):
        return f"Error deleting file {self.file_name}: {self.message}"

Ancestors

  • builtins.Exception
  • builtins.BaseException
class GoogleDriveAPIError (message: str)

Raised when there is an error connecting to Google Drive API.

Expand source code
class GoogleDriveAPIError(Exception):
    """Raised when there is an error connecting to Google Drive API."""
    def __init__(self, message: str):
        self.message = message
    def __str__(self):
        return f"Google Drive API error: {self.message}"

Ancestors

  • builtins.Exception
  • builtins.BaseException
class GoogleDriveHandler (credentials_file: str)
Expand source code
class GoogleDriveHandler:
    def __init__(self, credentials_file: str) -> None:
        if self.check_internet_connectivity() == False:
            raise InternetConnectivityError()
        try:
            self.credentials = service_account.Credentials.from_service_account_file(credentials_file, scopes=['https://www.googleapis.com/auth/drive'])
            self.drive_service = build('drive', 'v3', credentials=self.credentials)
        except (FileNotFoundError, json.JSONDecodeError) as e:
            raise LoadCredentialsError(str(e))
        except (IOError, ValueError) as e:
            raise InvalidCredentialsError(str(e))
        except HttpError as e:
            raise GoogleDriveAPIError(str(e))
        
    def check_internet_connectivity(self) -> bool:
        """Checks if there is an active internet connection."""
        try:
            requests.get('https://www.google.com', timeout=5)
            return True
        except requests.ConnectionError:
            return False
            
    def find_existing_files(self, file_name: str, parent_id: str) -> Optional[List[Dict[str, str]]]:
        """Finds all existing files with the same name in the target folder"""
        try: 
            query = f"name='{file_name}' and '{parent_id}' in parents and trashed=false"
            response = self.drive_service.files().list(q=query, fields='files(id, name)').execute()
            files = response.get('files', [])
            print(f'Existing files: {files}')
            return files if files else None
        except HttpError as e:
            if e.resp.status in [403, 404]:
                raise InvalidParentIDError(parent_id)
            raise FileQueryError(str(e))

    def delete_files(self, files: List[dict]) -> None:
        """Deletes files from Google Drive"""
        for file in files:
            try:
                self.drive_service.files().delete(fileId=file['id']).execute()
                print(f"Deleted file: {file['name']} (ID: {file['id']})")
            except HttpError as e:
                FileDeletionError(file['name'], file['id'], str(e))
    
    def delete_older_files(self, parent_id: str, num_files: int) -> None:
        """Deletes older files if there are more than 3 files in the target folder"""
        try:
            query = f"'{parent_id}' in parents and trashed=false"
            response = self.drive_service.files().list(q=query, fields='files(id, name)', orderBy='createdTime asc').execute()
            files = response.get('files', [])
            if len(files) > num_files:
                for file in files[0:len(files)-num_files]:
                    try:
                        self.drive_service.files().delete(fileId=file['id']).execute()
                        print(f"Deleted file: {file['name']}")
                    except HttpError as e:
                        FileDeletionError(file['name'], file['id'], str(e))
        except HttpError as e:
            if e.resp.status in [403, 404]:
                raise InvalidParentIDError(parent_id)
            raise FileQueryError(str(e))

    def upload_file_to_drive(self, file_name: str, parent_id: str) -> dict:
        """Uploads a file to Google Drive"""
        media = MediaFileUpload(file_name, mimetype='application/gzip', resumable=True)
        file_metadata = {
            'name': file_name,
            'parents': [parent_id]
        }
        
        try:
            request = self.drive_service.files().create(
                body=file_metadata,
                media_body=media,
                fields='id'
            )
            response = None
            while response is None:
                status, response = request.next_chunk()
                if status:
                    print(f"Uploaded {int(status.progress() * 100)}%")
            print(f"File uploaded successfully! File Id: {response.get('id')}")
            return response
        except HttpError as e:
            if e.resp.status in [403, 404]:
                raise InvalidParentIDError(parent_id)
            FileUploadError(file_name, str(e))
            raise e
    
    # this implementation overwrites any previous files with the same name in the target folder, essentially keeping only the latest file
    def overwrite_and_upload_to_drive(self, file_name: str, parent_id: str) -> None:
        """Uploads a file to Google Drive and deletes any previous files with the same name in the target folder"""
        existing_files = self.find_existing_files(file_name, parent_id)

        try:
            response = self.upload_file_to_drive(file_name, parent_id)
            if existing_files:
                self.delete_files(existing_files)

        except (FileUploadError, FileDeletionError, FileQueryError) as e:
            print(f'Unable to overwrite and upload file, error: {e}')

    # this implementation keeps the 'num_files' newest files at any point in time in the target folder
    def upload_to_drive_with_rfh(self, file_name: str, parent_id: str, num_files: int) -> None:
        """Uploads a file to Google Drive and deletes older files if there are more than 3 files in the target folder"""
        try:
            response = self.upload_file_to_drive(file_name, parent_id)
            self.delete_older_files(parent_id, num_files) 

        except (FileUploadError, FileDeletionError, FileQueryError) as e:
            print(f'Unable to upload file with RFH, error: {e}')

Methods

def check_internet_connectivity(self) ‑> bool

Checks if there is an active internet connection.

def delete_files(self, files: List[dict]) ‑> None

Deletes files from Google Drive

def delete_older_files(self, parent_id: str, num_files: int) ‑> None

Deletes older files if there are more than 3 files in the target folder

def find_existing_files(self, file_name: str, parent_id: str) ‑> Optional[List[Dict[str, str]]]

Finds all existing files with the same name in the target folder

def overwrite_and_upload_to_drive(self, file_name: str, parent_id: str) ‑> None

Uploads a file to Google Drive and deletes any previous files with the same name in the target folder

def upload_file_to_drive(self, file_name: str, parent_id: str) ‑> dict

Uploads a file to Google Drive

def upload_to_drive_with_rfh(self, file_name: str, parent_id: str, num_files: int) ‑> None

Uploads a file to Google Drive and deletes older files if there are more than 3 files in the target folder

class InternetConnectivityError

Raised when there is no active internet connection.

Expand source code
class InternetConnectivityError(Exception):
    """Raised when there is no active internet connection."""
    def __init__(self):
        pass
    def __str__(self):
        return "No internet connection"

Ancestors

  • builtins.Exception
  • builtins.BaseException
class InvalidCredentialsError (message: str)

Raised when credentials are invalid

Expand source code
class InvalidCredentialsError(Exception):
    """Raised when credentials are invalid"""
    def __init__(self, message: str):
        self.message = message
    def __str__(self):
        return f"Invalid credentials: {self.message}"

Ancestors

  • builtins.Exception
  • builtins.BaseException
class InvalidParentIDError (parent_id: str)

Raised when the parent ID is invalid or does not exist.

Expand source code
class InvalidParentIDError(Exception):
    """Raised when the parent ID is invalid or does not exist."""
    def __init__(self, parent_id: str):
        self.parent_id = parent_id
    def __str__(self):
        return f"Invalid parent ID: {self.parent_id}. The folder may not exist or the ID is incorrect."

Ancestors

  • builtins.Exception
  • builtins.BaseException
class LoadCredentialsError (message: str)

Raised when there is an error loading credentials from file.

Expand source code
class LoadCredentialsError(Exception):
    """Raised when there is an error loading credentials from file."""
    def __init__(self, message: str):
        self.message = message
    def __str__(self):
        return f"Error in reading the credentials file: {self.message}"

Ancestors

  • builtins.Exception
  • builtins.BaseException