-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from shipyardapp/sc-3280/tableau-verify-datasou…
…rce-refresh-status Sc 3280/tableau verify datasource refresh status
- Loading branch information
Showing
11 changed files
with
571 additions
and
3 deletions.
There are no files selected for viewing
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
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,49 @@ | ||
import sys | ||
import tableauserverclient as TSC | ||
|
||
EXIT_CODE_INVALID_CREDENTIALS = 200 | ||
|
||
|
||
def connect_to_tableau( | ||
username, | ||
password, | ||
site_id, | ||
server_url, | ||
sign_in_method): | ||
"""TSC library to sign in and sign out of Tableau Server and Tableau Online. | ||
:param username:The username or access token name of the user. | ||
:param password:The password or access token of the user. | ||
:param site_id: The site_id for required datasources. ex: ffc7f88a-85a7-48d5-ac03-09ef0a677280 | ||
:param server_url: This corresponds to the contentUrl attribute in the Tableau REST API. | ||
:param sign_in_method: Whether to log in with username_password or access_token. | ||
:return: server object, connection object | ||
""" | ||
if sign_in_method == 'username_password': | ||
tableau_auth = TSC.TableauAuth(username, password, site_id=site_id) | ||
|
||
if sign_in_method == 'access_token': | ||
tableau_auth = TSC.PersonalAccessTokenAuth( | ||
token_name=username, | ||
personal_access_token=password, | ||
site_id=site_id) | ||
|
||
try: | ||
# Make sure we use an updated version of the rest apis. | ||
server = TSC.Server( | ||
server_url, | ||
use_server_version=True, | ||
) | ||
connection = server.auth.sign_in(tableau_auth) | ||
print("Successfully authenticated with Tableau.") | ||
except Exception as e: | ||
print(f'Failed to connect to Tableau.') | ||
if sign_in_method == 'username_password': | ||
print('Invalid username or password. Please check for typos and try again.') | ||
if sign_in_method == 'access_token': | ||
print( | ||
'Invalid token name or access token. Please check for typos and try again.') | ||
print(e) | ||
sys.exit(EXIT_CODE_INVALID_CREDENTIALS) | ||
|
||
return server, connection |
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,148 @@ | ||
import argparse | ||
import sys | ||
import shipyard_utils as shipyard | ||
|
||
import tableauserverclient as TSC | ||
|
||
try: | ||
import authorization | ||
import errors | ||
import lookup | ||
except BaseException: | ||
from . import authorization | ||
from . import errors | ||
from . import lookup | ||
|
||
|
||
def get_args(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('--username', dest='username', required=True) | ||
parser.add_argument('--password', dest='password', required=True) | ||
parser.add_argument( | ||
'--sign-in-method', | ||
dest='sign_in_method', | ||
default='username_password', | ||
choices={ | ||
'username_password', | ||
'access_token'}, | ||
required=False) | ||
parser.add_argument('--site-id', dest='site_id', required=True) | ||
parser.add_argument('--server-url', dest='server_url', required=True) | ||
parser.add_argument('--view-name', dest='view_name', required=True) | ||
parser.add_argument( | ||
'--file-type', | ||
dest='file_type', | ||
choices=[ | ||
'png', | ||
'pdf', | ||
'csv'], | ||
type=str.lower, | ||
required=True) | ||
parser.add_argument( | ||
'--destination-file-name', | ||
dest='destination_file_name', | ||
default='output.csv', | ||
required=True) | ||
parser.add_argument( | ||
'--destination-folder-name', | ||
dest='destination_folder_name', | ||
default='', | ||
required=False) | ||
parser.add_argument('--file-options', dest='file_options', required=False) | ||
parser.add_argument('--workbook-name', dest='workbook_name', required=True) | ||
parser.add_argument('--project-name', dest='project_name', required=True) | ||
args = parser.parse_args() | ||
return args | ||
|
||
|
||
def generate_view_content(server, view_id, file_type): | ||
""" | ||
Given a specific view_id, populate the view and return the bytes necessary for creating the file. | ||
""" | ||
view_object = server.views.get_by_id(view_id) | ||
if file_type == 'png': | ||
server.views.populate_image(view_object) | ||
view_content = view_object.image | ||
if file_type == 'pdf': | ||
server.views.populate_pdf(view_object, req_options=None) | ||
view_content = view_object.pdf | ||
if file_type == 'csv': | ||
server.views.populate_csv(view_object, req_options=None) | ||
view_content = view_object.csv | ||
return view_content | ||
|
||
|
||
def write_view_content_to_file( | ||
destination_full_path, | ||
view_content, | ||
file_type, | ||
view_name): | ||
""" | ||
Write the byte contents to the specified file path. | ||
""" | ||
try: | ||
with open(destination_full_path, 'wb') as f: | ||
if file_type == 'csv': | ||
f.writelines(view_content) | ||
else: | ||
f.write(view_content) | ||
print( | ||
f'Successfully downloaded {view_name} to {destination_full_path}') | ||
except OSError as e: | ||
print(f'Could not write file: {destination_full_path}') | ||
print(e) | ||
sys.exit(errors.EXIT_CODE_FILE_WRITE_ERROR) | ||
|
||
|
||
def main(): | ||
args = get_args() | ||
username = args.username | ||
password = args.password | ||
site_id = args.site_id | ||
server_url = args.server_url | ||
sign_in_method = args.sign_in_method | ||
view_name = args.view_name | ||
file_type = args.file_type | ||
project_name = args.project_name | ||
workbook_name = args.workbook_name | ||
|
||
# Set all file parameters | ||
destination_file_name = args.destination_file_name | ||
destination_folder_name = shipyard.files.clean_folder_name( | ||
args.destination_folder_name) | ||
destination_full_path = shipyard.files.combine_folder_and_file_name( | ||
folder_name=destination_folder_name, file_name=destination_file_name) | ||
|
||
server, connection = authorization.connect_to_tableau( | ||
username, | ||
password, | ||
site_id, | ||
server_url, | ||
sign_in_method) | ||
|
||
with connection: | ||
project_id = lookup.get_project_id( | ||
server=server, project_name=project_name) | ||
workbook_id = lookup.get_workbook_id( | ||
server=server, | ||
project_id=project_id, | ||
workbook_name=workbook_name) | ||
view_id = lookup.get_view_id( | ||
server=server, | ||
project_id=project_id, | ||
workbook_id=workbook_id, | ||
view_name=view_name) | ||
|
||
view_content = generate_view_content( | ||
server=server, view_id=view_id, file_type=file_type) | ||
shipyard.files.create_folder_if_dne( | ||
destination_folder_name=destination_folder_name) | ||
write_view_content_to_file( | ||
destination_full_path=destination_full_path, | ||
view_content=view_content, | ||
file_type=file_type, | ||
view_name=view_name) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
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,16 @@ | ||
EXIT_CODE_FINAL_STATUS_SUCCESS = 0 | ||
EXIT_CODE_UNKNOWN_ERROR = 3 | ||
|
||
EXIT_CODE_FILE_WRITE_ERROR = 100 | ||
|
||
EXIT_CODE_INVALID_CREDENTIALS = 200 | ||
EXIT_CODE_INVALID_PROJECT = 201 | ||
EXIT_CODE_INVALID_WORKBOOK = 202 | ||
EXIT_CODE_INVALID_VIEW = 203 | ||
EXIT_CODE_INVALID_JOB = 204 | ||
EXIT_CODE_INVALID_DATASOURCE = 205 | ||
EXIT_CODE_REFRESH_ERROR = 206 | ||
|
||
EXIT_CODE_FINAL_STATUS_CANCELLED = 210 | ||
EXIT_CODE_FINAL_STATUS_ERRORED = 211 | ||
EXIT_CODE_STATUS_INCOMPLETE = 212 |
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,104 @@ | ||
import tableauserverclient as TSC | ||
import argparse | ||
import sys | ||
import shipyard_utils as shipyard | ||
|
||
try: | ||
import errors | ||
import authorization | ||
except BaseException: | ||
from . import errors | ||
from . import authorization | ||
|
||
|
||
def get_args(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('--username', dest='username', required=True) | ||
parser.add_argument('--password', dest='password', required=True) | ||
parser.add_argument('--site-id', dest='site_id', required=True) | ||
parser.add_argument('--server-url', dest='server_url', required=True) | ||
parser.add_argument( | ||
'--sign-in-method', | ||
dest='sign_in_method', | ||
default='username_password', | ||
choices={ | ||
'username_password', | ||
'access_token'}, | ||
required=False) | ||
parser.add_argument('--job-id', dest='job_id', required=False) | ||
args = parser.parse_args() | ||
return args | ||
|
||
|
||
def get_job_info(server, job_id): | ||
""" | ||
Gets information about the specified job_id. | ||
""" | ||
try: | ||
job_info = server.jobs.get_by_id(job_id) | ||
except Exception as e: | ||
print(f'Job {job_id} was not found.') | ||
print(e) | ||
sys.exit(errors.EXIT_CODE_INVALID_JOB) | ||
return job_info | ||
|
||
|
||
def determine_job_status(server, job_id): | ||
""" | ||
Job status response handler. | ||
The finishCode indicates the status of the job: -1 for incomplete, 0 for success, 1 for error, or 2 for cancelled. | ||
""" | ||
job_info = get_job_info(server, job_id) | ||
if job_info.finish_code == -1: | ||
if job_info.started_at is None: | ||
print( | ||
f'Tableau reports that the job {job_id} has not yet started.') | ||
else: | ||
print( | ||
f'Tableau reports that the job {job_id} is not yet complete.') | ||
exit_code = errors.EXIT_CODE_STATUS_INCOMPLETE | ||
elif job_info.finish_code == 0: | ||
print(f'Tableau reports that job {job_id} was successful.') | ||
exit_code = errors.EXIT_CODE_FINAL_STATUS_SUCCESS | ||
elif job_info.finish_code == 1: | ||
print(f'Tableau reports that job {job_id} errored.') | ||
exit_code = errors.EXIT_CODE_FINAL_STATUS_ERRORED | ||
elif job_info.finish_code == 2: | ||
print(f'Tableau reports that job {job_id} was cancelled.') | ||
exit_code = errors.EXIT_CODE_FINAL_STATUS_CANCELLED | ||
else: | ||
print(f'Something went wrong when fetching status for job {job_id}') | ||
exit_code = errors.EXIT_CODE_UNKNOWN_ERROR | ||
return exit_code | ||
|
||
|
||
def main(): | ||
args = get_args() | ||
username = args.username | ||
password = args.password | ||
site_id = args.site_id | ||
server_url = args.server_url | ||
sign_in_method = args.sign_in_method | ||
|
||
base_folder_name = shipyard.logs.determine_base_artifact_folder( | ||
'tableau') | ||
artifact_subfolder_paths = shipyard.logs.determine_artifact_subfolders( | ||
base_folder_name) | ||
shipyard.logs.create_artifacts_folders(artifact_subfolder_paths) | ||
|
||
if args.job_id: | ||
job_id = args.job_id | ||
else: | ||
job_id = shipyard.logs.read_pickle_file( | ||
artifact_subfolder_paths, 'job_id') | ||
|
||
server, connection = authorization.connect_to_tableau( | ||
username, password, site_id, server_url, sign_in_method) | ||
|
||
with connection: | ||
sys.exit(determine_job_status(server, job_id)) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Oops, something went wrong.