Skip to content

Commit

Permalink
Improve RPA.Cloud.Google Python usage (#1157)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikahanninen authored Mar 14, 2024
1 parent d2fb72b commit 73a4a28
Show file tree
Hide file tree
Showing 17 changed files with 404 additions and 238 deletions.
7 changes: 7 additions & 0 deletions docs/source/releasenotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ Latest versions
- Add keyword ``Merge range`` for merging cells in a range.
- Add keyword ``Unmerge range`` for unmerging cells in a range.

- Library **RPA.Cloud.Google** (:pr:`1157``, `rpaframework-google`` **9.0.0**):

- Fix problem with method intellisense in Python development environments.
- Add keyword ``Detect Tables`` for detecting table-like structures in spreadsheet's sheets.
- Add keyword ``Get Sheet Formulas`` for getting formulas from a sheet.
- Add keyword ``To A1 notation`` for converting column number to A1 notation.

28.3.0 - 22 Feb 2024
--------------------

Expand Down
2 changes: 1 addition & 1 deletion packages/google/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "rpaframework-google"
version = "8.2.0"
version = "9.0.0"
description = "Google library for RPA Framework"
authors = ["RPA Framework <[email protected]>"]
license = "Apache-2.0"
Expand Down
67 changes: 47 additions & 20 deletions packages/google/src/RPA/Cloud/Google/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import importlib
import logging
import os
from robotlibcore import DynamicCore


from .keywords.context import LibraryContext
from .keywords import (
AppsScriptKeywords,
BaseKeywords,
Expand All @@ -30,7 +30,21 @@ def import_vault():
return None


class Google(DynamicCore):
class Google(
AppsScriptKeywords,
BaseKeywords,
DocumentAIKeywords,
DriveKeywords,
GmailKeywords,
NaturalLanguageKeywords,
SheetsKeywords,
SpeechToTextKeywords,
StorageKeywords,
TextToSpeechKeywords,
TranslationKeywords,
VideoIntelligenceKeywords,
VisionKeywords,
): # pylint: disable=too-many-ancestors
"""`Google` is a library for operating with Google API endpoints.
**Installation**
Expand Down Expand Up @@ -124,21 +138,34 @@ def __init__(
if self.service_account_file is None:
self.service_account_file = os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
self.secrets_library = import_vault()

# Register keyword libraries to LibCore
libraries = [
AppsScriptKeywords(self),
BaseKeywords(self),
DocumentAIKeywords(self),
DriveKeywords(self),
GmailKeywords(self),
NaturalLanguageKeywords(self),
SheetsKeywords(self),
SpeechToTextKeywords(self),
StorageKeywords(self),
TextToSpeechKeywords(self),
TranslationKeywords(self),
VideoIntelligenceKeywords(self),
VisionKeywords(self),
]
super().__init__(libraries)
ctx = LibraryContext(self)
AppsScriptKeywords.__init__(self, ctx)
BaseKeywords.__init__(self, ctx)
DocumentAIKeywords.__init__(self, ctx)
DriveKeywords.__init__(self, ctx)
GmailKeywords.__init__(self, ctx)
NaturalLanguageKeywords.__init__(self, ctx)
SheetsKeywords.__init__(self, ctx)
SpeechToTextKeywords.__init__(self, ctx)
StorageKeywords.__init__(self, ctx)
TextToSpeechKeywords.__init__(self, ctx)
TranslationKeywords.__init__(self, ctx)
VideoIntelligenceKeywords.__init__(self, ctx)
VisionKeywords.__init__(self, ctx)


__all__ = [
"AppsScriptKeywords",
"BaseKeywords",
"DocumentAIKeywords",
"DriveKeywords",
"GmailKeywords",
"NaturalLanguageKeywords",
"SheetsKeywords",
"SpeechToTextKeywords",
"StorageKeywords",
"TextToSpeechKeywords",
"TranslationKeywords",
"VideoIntelligenceKeywords",
"VisionKeywords",
]
17 changes: 7 additions & 10 deletions packages/google/src/RPA/Cloud/Google/keywords/apps_script.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
from typing import Optional

from . import (
LibraryContext,
keyword,
)
from . import keyword


class AppsScriptKeywords(LibraryContext):
class AppsScriptKeywords:
"""Class for Google Apps Script API
For more information about Google Apps Script API link_.
Expand All @@ -15,8 +12,8 @@ class AppsScriptKeywords(LibraryContext):
"""

def __init__(self, ctx):
super().__init__(ctx)
self.service = None
self.ctx = ctx
self.script_service = None

@keyword(tags=["init", "apps script"])
def init_apps_script(
Expand All @@ -38,7 +35,7 @@ def init_apps_script(
apps_scopes = ["script.projects", "drive.scripts", "script.external_request"]
if scopes:
apps_scopes += scopes
self.service = self.init_service(
self.script_service = self.ctx.init_service(
service_name="script",
api_version="v1",
scopes=apps_scopes,
Expand All @@ -47,7 +44,7 @@ def init_apps_script(
use_robocorp_vault=use_robocorp_vault,
token_file=token_file,
)
return self.service
return self.script_service

@keyword(tags=["apps script"])
def run_script(
Expand All @@ -74,7 +71,7 @@ def run_script(
if parameters:
request["parameters"] = [parameters]
response = (
self.service.scripts()
self.script_service.scripts()
.run(
body=request,
scriptId=script_id,
Expand Down
7 changes: 5 additions & 2 deletions packages/google/src/RPA/Cloud/Google/keywords/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from . import LibraryContext, keyword
from . import keyword


class BaseKeywords(LibraryContext):
class BaseKeywords:
"""Base keywords for the Google library"""

def __init__(self, ctx):
self.ctx = ctx

@keyword
def set_robocorp_vault(
self,
Expand Down
55 changes: 18 additions & 37 deletions packages/google/src/RPA/Cloud/Google/keywords/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,7 @@ class LibraryContext:

def __init__(self, ctx):
self.ctx = ctx

@property
def logger(self):
return self.ctx.logger

@property
def robocorp_vault_name(self):
return self.ctx.robocorp_vault_name

@property
def robocorp_vault_secret_key(self):
return self.ctx.robocorp_vault_secret_key

@property
def use_robocorp_vault(self):
return self.ctx.use_robocorp_vault

@property
def service_account_file(self):
return self.ctx.service_account_file

@property
def cloud_auth_type(self):
return self.ctx.cloud_auth_type
self.logger = ctx.logger

def get_secret_from_robocorp_vault(self, secret_type="serviceaccount"):
if self.ctx.secrets_library is None:
Expand Down Expand Up @@ -105,7 +82,7 @@ def init_service(
"""
service = None
credentials = None
self.logger.debug("Init service with scopes: %s", scopes)
self.ctx.logger.debug("Init service with scopes: %s", scopes)
scopes = [f"https://www.googleapis.com/auth/{scope}" for scope in scopes]
if use_robocorp_vault is not None:
use_cloud = bool(use_robocorp_vault)
Expand All @@ -122,17 +99,17 @@ def init_service(
save_token,
)
elif service_account_file:
self.logger.info("Authenticating with service account file")
self.ctx.logger.info("Authenticating with service account file")
credentials = oauth_service_account.Credentials.from_service_account_file(
service_account_file, scopes=scopes
)
elif token_file:
self.logger.info("Authenticating with oauth token file")
self.ctx.logger.info("Authenticating with oauth token file")
credentials = self.get_credentials_with_oauth_token(
use_cloud, token_file, credentials_file, scopes, save_token
)
elif self.ctx.service_account_file:
self.logger.info("Authenticating with service account file")
self.ctx.logger.info("Authenticating with service account file")
credentials = oauth_service_account.Credentials.from_service_account_file(
self.ctx.service_account_file, scopes=scopes
)
Expand Down Expand Up @@ -172,24 +149,24 @@ def init_service_with_object(
client_object, cloud_auth_type, service_account_file, **kwargs
)
elif service_account_file:
self.logger.info("Authenticating with service account file")
self.ctx.logger.info("Authenticating with service account file")
service = client_object.from_service_account_json(
service_account_file, **kwargs
)
elif token_file:
self.logger.info("Authenticating with oauth token file")
self.ctx.logger.info("Authenticating with oauth token file")
token_file_location = Path(token_file).absolute()
if os.path.exists(token_file_location):
with open(token_file_location, "rb") as token:
credentials = pickle.loads(token)
service = client_object(credentials=credentials, **kwargs)
elif self.ctx.service_account_file:
self.logger.info("Authenticating with service account file")
self.ctx.logger.info("Authenticating with service account file")
service = client_object.from_service_account_json(
self.ctx.service_account_file, **kwargs
)
else:
self.logger.info("Authenticating with default client object")
self.ctx.logger.info("Authenticating with default client object")
service = client_object(**kwargs)

if service is None:
Expand Down Expand Up @@ -244,7 +221,7 @@ def get_credentials_with_oauth_token(
pickle.dumps(credentials)
).decode("utf-8")
self.ctx.secrets_library().set_secret(secrets)
self.logger.debug("Credentials refreshed")
self.ctx.logger.debug("Credentials refreshed")
if not credentials:
raise GoogleOAuthAuthenticationError(
"Could not get Google OAuth credentials"
Expand All @@ -262,15 +239,17 @@ def get_credentials_from_robocorp_vault(
):
credentials = None
if cloud_auth_type == "serviceaccount":
self.logger.info(
self.ctx.logger.info(
"Authenticating with service account file from Robocorp Vault"
)
service_account_file = self.get_secret_from_robocorp_vault("serviceaccount")
credentials = oauth_service_account.Credentials.from_service_account_file(
service_account_file, scopes=scopes
)
else:
self.logger.info("Authenticating with oauth token file from Robocorp Vault")
self.ctx.logger.info(
"Authenticating with oauth token file from Robocorp Vault"
)
credentials = self.get_credentials_with_oauth_token(
True,
token_file,
Expand All @@ -286,7 +265,7 @@ def get_service_from_robocorp_vault(
service = None
if cloud_auth_type == "serviceaccount":
try:
self.logger.info(
self.ctx.logger.info(
"Authenticating with service account file from Robocorp Vault"
)
service_account_file = self.get_secret_from_robocorp_vault(
Expand All @@ -299,7 +278,9 @@ def get_service_from_robocorp_vault(
if service_account_file:
os.remove(service_account_file)
else:
self.logger.info("Authenticating with oauth token file from Robocorp Vault")
self.ctx.logger.info(
"Authenticating with oauth token file from Robocorp Vault"
)
token = self.get_secret_from_robocorp_vault("token")
credentials = pickle.loads(base64.b64decode(token))
service = client_object(credentials=credentials, **kwargs)
Expand Down
26 changes: 14 additions & 12 deletions packages/google/src/RPA/Cloud/Google/keywords/document_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from google.api_core.client_options import ClientOptions
from google.cloud import documentai_v1 as documentai

from . import LibraryContext, keyword
from . import keyword


class DocumentAIKeywords(LibraryContext):
class DocumentAIKeywords:
"""Keywords for Google Cloud Document AI service.
Added on **rpaframework-google** version: 6.1.1
Expand All @@ -25,8 +25,8 @@ class DocumentAIKeywords(LibraryContext):
"""

def __init__(self, ctx):
super().__init__(ctx)
self.service = None
self.ctx = ctx
self.ai_service = None

@keyword(name="Init Document AI", tags=["init", "document ai"])
def init_document_ai(
Expand Down Expand Up @@ -73,15 +73,15 @@ def init_document_ai(
api_endpoint=f"{region.lower()}-documentai.googleapis.com"
)
kwargs["client_options"] = opts
self.logger.info(f"Using Document AI from '{region.upper()}' region")
self.service = self.init_service_with_object(
self.ctx.logger.info(f"Using Document AI from '{region.upper()}' region")
self.ai_service = self.ctx.init_service_with_object(
documentai.DocumentProcessorServiceClient,
service_account,
use_robocorp_vault,
token_file,
**kwargs,
)
return self.service
return self.ai_service

@keyword(tags=["document ai"])
def process_document(
Expand Down Expand Up @@ -141,21 +141,23 @@ def process_document(
for lang in languages:
print(lang)
""" # noqa: E501
name = self.service.processor_path(project_id, region, processor_id)
name = self.ai_service.processor_path(project_id, region, processor_id)

# Read the file into memory
with open(file_path, "rb") as binary:
binary_content = binary.read()

mime = mime_type or mimetypes.guess_type(file_path)[0]
self.logger.info(f"Processing document '{file_path}' with mimetype '{mime}'")
self.ctx.logger.info(
f"Processing document '{file_path}' with mimetype '{mime}'"
)
# Load Binary Data into Document AI RawDocument Object
raw_document = documentai.RawDocument(content=binary_content, mime_type=mime)

# Configure the process request
request = documentai.ProcessRequest(name=name, raw_document=raw_document)

result = self.service.process_document(request=request)
result = self.ai_service.process_document(request=request)

document = result.document
return document
Expand Down Expand Up @@ -314,10 +316,10 @@ def list_processors(self, project_id: str, region: str) -> List:
print(f"Processor type: {p.type_}")
print(f"Processor name: {p.display_name}")
"""
parent_value = self.service.common_location_path(project_id, region)
parent_value = self.ai_service.common_location_path(project_id, region)
# Initialize request argument(s)
request = documentai.ListProcessorsRequest(
parent=parent_value,
)
processor_list = self.service.list_processors(request=request)
processor_list = self.ai_service.list_processors(request=request)
return processor_list
Loading

0 comments on commit 73a4a28

Please sign in to comment.