-
Notifications
You must be signed in to change notification settings - Fork 178
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'edge' into app_odd-anonymous-localization-provider
- Loading branch information
Showing
532 changed files
with
41,134 additions
and
75,414 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Tools for Google API Interaction.""" |
152 changes: 152 additions & 0 deletions
152
abr-testing/abr_testing/automation/google_drive_tool.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
"""Google Drive Tool.""" | ||
import os | ||
from typing import Set, Any, Optional | ||
import webbrowser | ||
import mimetypes | ||
from oauth2client.service_account import ServiceAccountCredentials # type: ignore[import] | ||
from googleapiclient.discovery import build | ||
from googleapiclient.http import MediaFileUpload | ||
|
||
"""Google Drive Tool. | ||
This module requires a credentials.json file before getting started. | ||
Retrieve from https://console.cloud.google.com/apis/credentials.""" | ||
|
||
|
||
class google_drive: | ||
"""Google Drive Tool.""" | ||
|
||
def __init__(self, credentials: Any, folder_name: str, email: str) -> None: | ||
"""Connects to google drive via credentials file.""" | ||
self.scope = ["https://www.googleapis.com/auth/drive"] | ||
self.credentials = ServiceAccountCredentials.from_json_keyfile_name( | ||
credentials, self.scope | ||
) | ||
self.drive_service = build("drive", "v3", credentials=self.credentials) | ||
self.parent_folder = folder_name | ||
self.email = email | ||
self.folder = self.open_folder() | ||
|
||
def list_folder(self, delete: Any = False) -> Set[str]: | ||
"""List folders and files in Google Drive.""" | ||
file_names = set() | ||
page_token: str = "" | ||
while True: | ||
results = ( | ||
self.drive_service.files() | ||
.list( | ||
q=f"'{self.parent_folder}' in parents and trashed=false" | ||
if self.parent_folder | ||
else "" # type: ignore | ||
if self.parent_folder | ||
else None, | ||
pageSize=1000, | ||
fields="nextPageToken, files(id, name, mimeType)", | ||
pageToken=page_token, | ||
) | ||
.execute() | ||
) | ||
items = results.get("files", []) | ||
if not items: | ||
break | ||
for item in items: | ||
item_name = item["name"] | ||
if delete: | ||
self.delete_files(item["id"]) | ||
file_names.add(item_name) | ||
page_token = results.get("nextPageToken", "") | ||
if len(page_token) < 1: | ||
break | ||
if not file_names: | ||
print("No folders or files found in Google Drive.") | ||
print(f"{len(file_names)} item(s) in Google Drive") | ||
return file_names | ||
|
||
def delete_files(self, file_or_folder_id: str) -> None: | ||
"""Delete a file or folder in Google Drive by ID.""" | ||
try: | ||
self.drive_service.files().delete(fileId=file_or_folder_id).execute() | ||
print(f"Successfully deleted file/folder with ID: {file_or_folder_id}") | ||
except Exception as e: | ||
print(f"Error deleting file/folder with ID: {file_or_folder_id}") | ||
print(f"Error details: {str(e)}") | ||
|
||
def upload_file(self, file_path: str) -> str: | ||
"""Upload file to Google Drive.""" | ||
file_metadata = { | ||
"name": os.path.basename(file_path), | ||
"mimeType": str(mimetypes.guess_type(file_path)[0]), | ||
"parents": [self.parent_folder], | ||
} | ||
media = MediaFileUpload(file_path, resumable=True) | ||
|
||
uploaded_file = ( | ||
self.drive_service.files() | ||
.create(body=file_metadata, media_body=media, fields="id") # type: ignore | ||
.execute() | ||
) | ||
return uploaded_file["id"] | ||
|
||
def upload_missing_files(self, storage_directory: str) -> None: | ||
"""Upload missing files to Google Drive.""" | ||
# Read Google Drive .json files. | ||
google_drive_files = self.list_folder() | ||
google_drive_files_json = [ | ||
file for file in google_drive_files if file.endswith(".json") | ||
] | ||
# Read local directory. | ||
local_files_json = set( | ||
file for file in os.listdir(storage_directory) if file.endswith(".json") | ||
) | ||
missing_files = local_files_json - set(google_drive_files_json) | ||
print(f"Missing files: {len(missing_files)}") | ||
# Upload missing files. | ||
uploaded_files = [] | ||
for file in missing_files: | ||
file_path = os.path.join(storage_directory, file) | ||
uploaded_file_id = google_drive.upload_file(self, file_path) | ||
self.share_permissions(uploaded_file_id) | ||
uploaded_files.append( | ||
{"name": os.path.basename(file_path), "id": uploaded_file_id} | ||
) | ||
# Fetch the updated file list after all files are uploaded | ||
files = google_drive.list_folder(self) | ||
file_names = [file for file in files] | ||
for uploaded_file in uploaded_files: | ||
this_name = uploaded_file["name"] | ||
if this_name in file_names: | ||
print( | ||
f"File '{this_name}' was successfully uploaded with ID: {uploaded_file['id']}" | ||
) | ||
else: | ||
print( | ||
f"File '{this_name}' was not found in the list of files after uploading." | ||
) | ||
|
||
def open_folder(self) -> Optional[str]: | ||
"""Open folder in web browser.""" | ||
folder_metadata = ( | ||
self.drive_service.files() | ||
.get(fileId=self.parent_folder, fields="webViewLink") | ||
.execute() | ||
) | ||
folder_link = folder_metadata.get("webViewLink") | ||
if folder_link: | ||
print(f"Folder link: {folder_link}") | ||
webbrowser.open( | ||
folder_link | ||
) # Open the folder link in the default web browser | ||
else: | ||
print("Folder link not found.") | ||
return folder_link | ||
|
||
def share_permissions(self, file_id: str) -> None: | ||
"""Share permissions with self.""" | ||
new_permission = { | ||
"type": "user", | ||
"role": "writer", | ||
"emailAddress": self.email, | ||
} | ||
self.drive_service.permissions().create( | ||
fileId=file_id, body=new_permission, transferOwnership=False # type: ignore | ||
).execute() |
115 changes: 115 additions & 0 deletions
115
abr-testing/abr_testing/automation/google_sheets_tool.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
"""Google Sheet Tool.""" | ||
import gspread # type: ignore[import] | ||
import socket | ||
import httplib2 | ||
from oauth2client.service_account import ServiceAccountCredentials # type: ignore[import] | ||
from typing import Dict, List, Any, Set, Tuple | ||
|
||
"""Google Sheets Tool. | ||
This module requires a credentials.json file before getting started. | ||
Retrieve from https://console.cloud.google.com/apis/credentials. | ||
""" | ||
|
||
|
||
class google_sheet: | ||
"""Google Sheets Tool.""" | ||
|
||
def __init__(self, credentials: Any, file_name: str, tab_number: int) -> None: | ||
"""Connects to google sheet via credentials file.""" | ||
self.scope = [ | ||
"https://spreadsheets.google.com/feeds", | ||
"https://www.googleapis.com/auth/drive", | ||
] | ||
self.credentials = ServiceAccountCredentials.from_json_keyfile_name( | ||
credentials, self.scope | ||
) | ||
self.gc = gspread.authorize(self.credentials) | ||
self.file_name = file_name | ||
self.tab_number = tab_number | ||
self.spread_sheet = self.open_google_sheet() | ||
self.worksheet = self.open_worksheet(self.tab_number) | ||
self.row_index = 1 | ||
|
||
def open_google_sheet(self) -> Any: | ||
"""Open Google Spread Sheet.""" | ||
sheet = self.gc.open(self.file_name) | ||
return sheet | ||
|
||
def open_worksheet(self, tab_number: int) -> Any: | ||
"""Open individual worksheet within a googlesheet.""" | ||
return self.spread_sheet.get_worksheet(tab_number) | ||
|
||
def create_worksheet(self, tab_name: int) -> None: | ||
"""Create a worksheet with tab name. Existing spreadsheet needed.""" | ||
try: | ||
self.spread_sheet.add_worksheet(tab_name, rows="1000", cols="26") | ||
except gspread.exceptions.APIError: | ||
print("Work Sheet already exists") | ||
|
||
def write_header(self, header: List) -> None: | ||
"""Write Header to first row if not present.""" | ||
header_list = self.worksheet.row_values(1) | ||
if header_list != header: | ||
self.worksheet.insert_row(header, self.row_index) | ||
|
||
def write_to_row(self, data: List) -> None: | ||
"""Write data into a row in a List[] format.""" | ||
try: | ||
self.row_index += 1 | ||
self.worksheet.insert_row(data, index=self.row_index) | ||
except socket.gaierror: | ||
pass | ||
except httplib2.ServerNotFoundError: | ||
print("UNABLE TO CONNECT TO SERVER!!, CHECK CONNECTION") | ||
except Exception as error: | ||
print(error.__traceback__) | ||
|
||
def delete_row(self, row_index: int) -> None: | ||
"""Delete Row from google sheet.""" | ||
self.worksheet.delete_row(row_index) | ||
|
||
def update_cell( | ||
self, row: int, column: int, single_data: Any | ||
) -> Tuple[int, int, Any]: | ||
"""Update ONE individual cell according to a row and column.""" | ||
self.worksheet.update_cell(row, column, single_data) | ||
return row, column, single_data | ||
|
||
def get_all_data(self) -> Dict[str, Any]: | ||
"""Get all the Data recorded from worksheet.""" | ||
return self.worksheet.get_all_records() | ||
|
||
def get_column(self, column_number: int) -> Set[str]: | ||
"""Get all values in column.""" | ||
return self.worksheet.col_values(column_number) | ||
|
||
def get_index_row(self) -> int: | ||
"""Check for the next available row to write too.""" | ||
row_index = len(self.get_column(1)) | ||
print("Row Index: ", row_index) | ||
return row_index | ||
|
||
def update_row_index(self) -> None: | ||
"""Update self.row_index instance variable.""" | ||
self.row_index = self.get_index_row() | ||
|
||
def get_all_sheets(self) -> List[str]: | ||
"""List all tabs in the spreadsheets.""" | ||
worksheets = self.spread_sheet.worksheets() | ||
return worksheets | ||
|
||
def get_sheet_by_name(self, title: str) -> None: | ||
"""Reference sheet by name.""" | ||
try: | ||
worksheet = self.spread_sheet.worksheet(title) | ||
return worksheet | ||
except gspread.exceptions.WorksheetNotFound: | ||
raise gspread.exceptions.WorksheetNotFound( | ||
"Worksheet does not exist!!, Use create_worksheet() function first." | ||
) | ||
|
||
def token_check(self) -> None: | ||
"""Check if still credentials are still logged in.""" | ||
if self.credentials.access_token_expired: | ||
self.gc.login() |
Oops, something went wrong.