diff --git a/README.md b/README.md index a6d4076..d822bdd 100644 --- a/README.md +++ b/README.md @@ -187,13 +187,20 @@ tableau_tools * tableau_http * tableau_emailer (legacy, unsupported) +#### 0.0.1 Examples +Within the installed package, there is an examples sub-directory with a bunch of functional examples as well as ones that start with "test_suite" that show how to use almost any method in the library. -### 0.1 Importing tableau_tools library -It is recommended that you import everything from the tableau_tools package like: +It may be easier just to find them all on GitHub at https://github.com/bryantbhowell/tableau_tools/tree/5.0.0/examples , and then download them to your own local directories and start trying them out. + +### 0.1 Importing tableau_tools library into your scripts +If you just import tableau_tools per the following, you will have access to the tableau_rest_api sub-package: from tableau_tools import * - from tableau_tools.tableau_rest_api import * - from tableau_tools.tableau_documents import * + +If you need to use tableau_documents, use the following import statements: + + from tableau_tools import * + from tableau_documents import * ### 0.2 Logger class The Logger class implements useful and verbose logging to a plain text file that all of the other objects can use. You declare a single Logger object, then pass it to the other objects, resulting in a single continuous log file of all actions. diff --git a/examples/archive_site.py b/examples/archive_site.py index d1e7af8..ec1c18d 100644 --- a/examples/archive_site.py +++ b/examples/archive_site.py @@ -1,44 +1,44 @@ # -*- coding: utf-8 -*- -from tableau_tools.tableau_rest_api import * +# from tableau_tools.tableau_rest_api import * from tableau_tools import * import os def archive_tableau_site(save_to_directory, server, username, password, site_content_url): # The last two digits of this constructor match to the version of API available on the Tableau Server - t = TableauRestApiConnection30(server=server, username=username, + t = TableauServerRest33(server=server, username=username, password=password, site_content_url=site_content_url) t.signin() - all_projects = t.query_projects() + all_projects = t.projects.query_projects() all_projects_dict = t.convert_xml_list_to_name_id_dict(all_projects) # This gives you the Project name; the values of the dict are the LUIDs for project in all_projects_dict: # Create directory for projects try: - print(('Making directory {}'.format(project))) + print('Making directory {}'.format(project)) os.mkdir('{}/{}'.format(save_to_directory, project)) except OSError as e: print('Directory already exists') - print(('Downloading datasources for project {}'.format(project))) + print('Downloading datasources for project {}'.format(project)) # Get All Data sources - dses_in_project = t.query_datasources(project_name_or_luid=all_projects_dict[project]) + dses_in_project = t.datasources.query_datasources(project_name_or_luid=all_projects_dict[project]) for ds in dses_in_project: ds_luid = ds.get('id') ds_content_url = ds.get('contentUrl') - print(('Downloading datasource {}'.format(ds_content_url))) - t.download_datasource(ds_name_or_luid=ds_luid, + print('Downloading datasource {}'.format(ds_content_url)) + t.datasources.download_datasource(ds_name_or_luid=ds_luid, filename_no_extension="{}/{}/{}".format(save_to_directory, project, ds_content_url), include_extract=False) - print(('Downloading workbooks for project {}'.format(project))) - wbs_in_project = t.query_workbooks_in_project(project_name_or_luid=all_projects_dict[project]) + print('Downloading workbooks for project {}'.format(project)) + wbs_in_project = t.workbooks.query_workbooks_in_project(project_name_or_luid=all_projects_dict[project]) for wb in wbs_in_project: wb_luid = wb.get('id') wb_content_url = wb.get('contentUrl') print(('Downloading workbook {}'.format(wb_content_url))) - t.download_workbook(wb_name_or_luid=wb_luid, + t.workbooks.download_workbook(wb_name_or_luid=wb_luid, filename_no_extension="{}/{}/{}".format(save_to_directory, project, wb_content_url), include_extract=False) \ No newline at end of file diff --git a/examples/create_site_sample.py b/examples/create_site_sample.py index 851707f..c42e521 100644 --- a/examples/create_site_sample.py +++ b/examples/create_site_sample.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -from ...tableau_tools.tableau_rest_api import * -from ...tableau_tools import * +from tableau_tools import * import time server = 'http://127.0.0.1' diff --git a/examples/extract_api_samples.py b/examples/hyper_api_samples.py similarity index 97% rename from examples/extract_api_samples.py rename to examples/hyper_api_samples.py index 2518934..86b6762 100644 --- a/examples/extract_api_samples.py +++ b/examples/hyper_api_samples.py @@ -1,9 +1,8 @@ # -*- coding: utf-8 -*- from tableau_tools import * -from tableau_tools.tableau_rest_api import * +from tableau_documents import * from tableau_tools.tableau_documents.hyper_file_generator import HyperFileGenerator -from tableau_tools.tableau_documents.tableau_file import TableauFile -from tableau_tools.tableau_documents.tableau_datasource import TableauDatasource, TableauConnection + import pyodbc import sys diff --git a/examples/move_extracts_from_server_to_server.py b/examples/move_extracts_from_server_to_server.py index c573fb2..a613d35 100644 --- a/examples/move_extracts_from_server_to_server.py +++ b/examples/move_extracts_from_server_to_server.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -from tableau_tools.tableau_rest_api import * from tableau_tools import * import time @@ -22,16 +21,20 @@ d_password = '' d_site_content_url = '' -t = TableauRestApiConnection28(o_server, o_username, o_password, o_site_content_url) + +t = TableauServerRest28(server=o_server, username=o_username, password=o_password, site_content_url=o_site_content_url) t.signin() t.enable_logging(logger) downloaded_filename = 'File Name' wb_name_on_server = 'WB Name on Server' proj_name = 'Default' -t.download_workbook('WB Name on Server', downloaded_filename, proj_name_or_luid=proj_name) +t.workbooks.download_workbook(wb_name_or_luid='WB Name on Server', filename_no_extension=downloaded_filename, + proj_name_or_luid=proj_name) -d = TableauRestApiConnection28(d_server, d_username, d_password, d_site_content_url) +d = TableauServerRest28(d_server, d_username, d_password, d_site_content_url) d.signin() d.enable_logging(logger) -proj = d.query_project('Default') -d.publish_workbook('{}.twbx'.format(downloaded_filename), wb_name_on_server, proj, save_credentials=False, overwrite=True) \ No newline at end of file +proj = d.projects.query_project('Default') +d.workbooks.publish_workbook(workbook_filename='{}.twbx'.format(downloaded_filename), + workbook_name=wb_name_on_server, project_obj=proj, + save_credentials=False, overwrite=True) \ No newline at end of file diff --git a/examples/permissions_auditing.py b/examples/permissions_auditing.py index 3ce5a23..d4d5c0b 100644 --- a/examples/permissions_auditing.py +++ b/examples/permissions_auditing.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- -from tableau_tools.tableau_rest_api import * from tableau_tools import * import csv -# There are differences between Python 2.7 and Python 3, so this may not run on Python 3 yet - username = '' password = '' server = 'http://localhost' logger = Logger('permissions.log') -default = TableauRestApiConnection25(server, username, password) +default = TableauRestApiConnection28(server=server, username=username, password=password) default.enable_logging(logger) default.signin() @@ -38,7 +35,8 @@ output_writer.writerow(headers) for site_content_url in site_content_urls: - t = TableauRestApiConnection28(server, username, password, site_content_url) + t = TableauRestApiConnection28(server=server, username=username, password=password, + site_content_url=site_content_url) t.enable_logging(logger) t.signin() projects = t.query_projects() diff --git a/examples/permissions_changing.py b/examples/permissions_changing.py deleted file mode 100644 index 616ebe1..0000000 --- a/examples/permissions_changing.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: utf-8 -*- - -from tableau_tools.tableau_rest_api import * -from tableau_tools import * - -def update_workbook_permissions(project_obj, published_workbook_object, group_luid, capabilities_dict): - """ - :type project_obj: Project - :type published_workbook_object: Workbook - :type group_luid: unicode - :type capabilities_dict: dict - :return: - """ - # Query the permissions objects (comes as a list) - permissions = published_workbook_object.get_permissions_obj_list() - print("Retrieved Permissions") - # Get the permissions object for the group_luid - # Have to check for group_luid not being set at all - does_group_have_any_permissions = False - for perm_obj in permissions: - - if perm_obj.luid == group_luid: - does_group_have_any_permissions = True - for cap in capabilities_dict: - perm_obj.set_capability(cap, capabilities_dict[cap]) - - # Send back the whole of the original list of permissions, with the one modified. - if does_group_have_any_permissions is True: - print('Updating Existing Permissions for Group') - published_workbook_object.set_permissions_by_permissions_obj_list(permissions) - - # If there are no permissions at all, create Permissions object for it - elif does_group_have_any_permissions is False: - new_perm_obj = project_obj.create_workbook_permissions_object_for_group(all_users_group_luid) - for cap in capabilities_dict: - new_perm_obj.set_capability(cap, capabilities_dict[cap]) - print('No permissions found for group, adding new permissions') - published_workbook_object.set_permissions_by_permissions_obj_list([new_perm_obj, ]) - -capabilities_to_set = {"Download Full Data": "Deny"} -tableau_group_name = 'All Users' - -server = 'http://' -username = 'username' -password = 'secure_password' -site = 'a_site' - -logger = Logger('Permissions.log') - -t = TableauRestApiConnection28(server=server, username=username, password=password, site_content_url=site) -t.signin() -t.enable_logging(logger) - -projects = t.query_projects() -projects_dict = t.convert_xml_list_to_name_id_dict(projects) - - -# Determine the identifer (LUID) of the Group -try: - all_users_group_luid = t.query_group_luid(group_name=tableau_group_name) - for project in projects_dict: - # List of projects we want to search in. Uncomment if you want to limit which projects are affected: - # projects_to_change = [u'Project A', u'Project C'] - # if project not in projects_to_change: - # continue - - # Update the workbook_defaults in the project - - # Get the project as an object so permissions are available - try: - project_object = t.query_project(project_name_or_luid=project) - workbook_defaults_obj = project_object.workbook_defaults - print("Updating the Project's Workbook Defaults") - update_workbook_permissions(project_obj=project_object, published_workbook_object=workbook_defaults_obj, - group_luid=all_users_group_luid, capabilities_dict=capabilities_to_set) - - # Update the workbooks themselves (if the permissions aren't locked, because this would be a waste of time) - if project_object.are_permissions_locked() is False: - wbs_in_project = t.query_workbooks_in_project(project_name_or_luid=project) - wbs_dict = t.convert_xml_list_to_name_id_dict(wbs_in_project) - for wb in wbs_dict: - # Second parameter project_name is unecessary when passing a LUID - # That is why you reference wbs_dict[wb], rather than wb directly, which is just the name - wb_obj = t.get_published_workbook_object(workbook_name_or_luid=wbs_dict[wb], - project_name_or_luid="") - print(('Updating workbook with LUID {}'.format(wbs_dict[wb]))) - update_workbook_permissions(project_obj=project_object, published_workbook_object=wb_obj, - group_luid=all_users_group_luid, capabilities_dict=capabilities_to_set) - - except NoMatchFoundException: - print("No project found with the given name, check the log") - exit() - -except NoMatchFoundException: - print("No group found using the name provided") - exit() - - - - - - - - diff --git a/examples/user_sync_sample.py b/examples/user_sync_sample.py index 0bc2130..5c105a2 100644 --- a/examples/user_sync_sample.py +++ b/examples/user_sync_sample.py @@ -1,13 +1,14 @@ -import psycopg2.extensions -import psycopg2 - -from tableau_rest_api.tableau_rest_api_connection import * - # This is example code showing a sync process from a database with users in it # It uses psycopg2 to connect to a PostgreSQL database # You can substitute in any source of the usernames and groups # The essential logic is that you do a full comparison on who should exist and who doesn't, and both add and remove + +import psycopg2.extensions +import psycopg2 + +from tableau_tools import * + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) @@ -18,7 +19,7 @@ password = '' server = 'http://' -t = TableauRestApiConnection28(server=server, username=username, password=password, site_content_url='default') +t = TableauServerRest33(server=server, username=username, password=password, site_content_url='default') t.enable_logging(logger) t.signin() @@ -36,8 +37,8 @@ # Loop through the results for row in cur: if row[0] not in groups_dict: - print(('Creating group {}'.format(row[0]))) - luid = t.create_group(group_name=row[0]) + print('Creating group {}'.format(row[0])) + luid = t.groups.create_group(group_name=row[0]) groups_dict[row[0]] = luid print(groups_dict) @@ -49,21 +50,21 @@ cur.execute(sql_statement) # Get all the users on the site -users = t.query_users() +users = t.users.query_users() users_dict = t.convert_xml_list_to_name_id_dict(users) # Loop through users, make sure they exist for row in cur: if row[0] not in users_dict: - print(('Creating user {}'.format(row[0].encode('utf8')))) - luid = t.add_user(username=row[0], fullname=row[1], site_role='Publisher') + print('Creating user {}'.format(row[0].encode('utf8'))) + luid = t.users.add_user(username=row[0], fullname=row[1], site_role='Publisher') users_dict[row[0]] = luid print(users_dict) # Create projects for each user for user in users_dict: - proj_obj = t.create_project("My Saved Reports - {}".format(user)) + proj_obj = t.projects.create_project("My Saved Reports - {}".format(user)) user_luid = users_dict[user] perms_obj = proj_obj.create_project_permissions_object_for_user(username_or_luid=user_luid, role='Publisher') proj_obj.set_permissions_by_permissions_obj_list([perms_obj, ]) @@ -89,23 +90,23 @@ groups_and_users[group_luid] = [] groups_and_users[group_luid].append(user_luid) - print(('Adding user {} to group {}'.format(row[0].encode('utf8'), row[2].encode('utf8')))) - t.add_users_to_group(username_or_luid_s=user_luid, group_name_or_luid=group_luid) + print('Adding user {} to group {}'.format(row[0].encode('utf8'), row[2].encode('utf8'))) + t.groups.add_users_to_group(username_or_luid_s=user_luid, group_name_or_luid=group_luid) # Determine if any users are in a group who do not belong, then remove them for group_luid in groups_and_users: if group_luid == groups_dict['All Users']: continue - users_in_group_on_server = t.query_users_in_group(group_luid) + users_in_group_on_server = t.groups.query_users_in_group(group_luid) users_in_group_on_server_dict = t.convert_xml_list_to_name_id_dict(users_in_group_on_server) # values() are the LUIDs in these dicts for user_luid in list(users_in_group_on_server_dict.values()): if user_luid not in groups_and_users[group_luid]: - print(('Removing user {} from group {}'.format(user_luid, group_luid))) - t.remove_users_from_group(username_or_luid_s=user_luid, group_name_or_luid=group_luid) + print('Removing user {} from group {}'.format(user_luid, group_luid)) + t.groups.remove_users_from_group(username_or_luid_s=user_luid, group_name_or_luid=group_luid) # Determine if there are any users who are in the system and not in the database, set them to unlicsened -users_on_server = t.query_users() +users_on_server = t.users.query_users() for user_on_server in users_on_server: # Skip the guest user if user_on_server.get("name") == 'guest': @@ -114,11 +115,4 @@ if user_on_server.get("siteRole") not in ['ServerAdministrator', 'SiteAdministrator']: print(('User on server {} not found in security table, set to Unlicensed'.format(user_on_server.get("name").encode('utf8')))) # Just set them to 'Unlicensed' - t.update_user(username_or_luid=user_on_server.get("name"), site_role='Unlicensed') - - -# You can check that content permissions all match their project permissions if necessary -# projects = t.query_projects() -# projects_dict = t.convert_xml_list_to_name_id_dict(projects) -# for proj_luid in projects_dict.values(): -# t.sync_project_permissions_to_contents(proj_luid) \ No newline at end of file + t.users.update_user(username_or_luid=user_on_server.get("name"), site_role='Unlicensed') diff --git a/tableau_rest_api_connection.py b/tableau_rest_api_connection.py index 60d93ea..9c3f483 100644 --- a/tableau_rest_api_connection.py +++ b/tableau_rest_api_connection.py @@ -30,53 +30,77 @@ def __init__(self, server: str, username: str, password: str, site_content_url: class TableauRestApiConnection27(WorkbookMethods27, UserMethods27, SubscriptionMethods27, SiteMethods27, ScheduleMethods27, RevisionMethods27, ProjectMethods27, GroupMethods27, FavoritesMethods27, ExtractMethods27, DatasourceMethods27, TableauRestApiBase27, TableauRestApiConnection): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection28(WorkbookMethods28, UserMethods28, SubscriptionMethods28, SiteMethods28, ScheduleMethods28, RevisionMethods28, ProjectMethods28, GroupMethods28, FavoritesMethods28, ExtractMethods28, DatasourceMethods28, TableauRestApiBase28, TableauRestApiConnection27): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection27.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection30(WorkbookMethods30, UserMethods30, SubscriptionMethods30, SiteMethods30, ScheduleMethods30, RevisionMethods30, ProjectMethods30, GroupMethods30, FavoritesMethods30, ExtractMethods30, DatasourceMethods30, TableauRestApiBase30, TableauRestApiConnection28): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection28.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection31(WorkbookMethods31, UserMethods31, SubscriptionMethods31, SiteMethods31, ScheduleMethods31, RevisionMethods31, ProjectMethods31, GroupMethods31, FavoritesMethods31, ExtractMethods31, DatasourceMethods31, TableauRestApiBase31, TableauRestApiConnection30): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection30.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection32(WorkbookMethods32, UserMethods32, SubscriptionMethods32, SiteMethods32, ScheduleMethods32, RevisionMethods32, ProjectMethods32, GroupMethods32, FavoritesMethods32, ExtractMethods32, DatasourceMethods32, AlertMethods32, TableauRestApiBase32, TableauRestApiConnection31): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection31.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection33(WorkbookMethods33, UserMethods33, SubscriptionMethods33, SiteMethods33, ScheduleMethods33, RevisionMethods33, ProjectMethods33, GroupMethods33, FlowMethods33, FavoritesMethods33, ExtractMethods33, DatasourceMethods33, AlertMethods33, TableauRestApiBase33, TableauRestApiConnection32): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection32.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection34(WorkbookMethods34, UserMethods34, SubscriptionMethods34, SiteMethods34, ScheduleMethods34, RevisionMethods34, ProjectMethods34, GroupMethods34, FlowMethods34, FavoritesMethods34, ExtractMethods34, DatasourceMethods34, AlertMethods34, TableauRestApiBase34, TableauRestApiConnection33): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection33.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection35(WorkbookMethods35, UserMethods35, SubscriptionMethods35, SiteMethods35, ScheduleMethods35, RevisionMethods35, ProjectMethods35, MetadataMethods35, GroupMethods35, FlowMethods35, FavoritesMethods35, ExtractMethods35, DatasourceMethods35, AlertMethods35, TableauRestApiBase35, TableauRestApiConnection34): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection34.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self class TableauRestApiConnection36(WorkbookMethods36, WebhooksMethods36, UserMethods36, SubscriptionMethods36, SiteMethods36, @@ -84,4 +108,7 @@ class TableauRestApiConnection36(WorkbookMethods36, WebhooksMethods36, UserMetho GroupMethods36, FlowMethods36, FavoritesMethods36, ExtractMethods36, DatasourceMethods36, AlertMethods36, TableauRestApiBase36, TableauRestApiConnection35): - pass + def __init__(self, server: str, username: str, password: str, site_content_url: Optional[str] = ""): + TableauRestApiConnection35.__init__(self, server=server, username=username, password=password, + site_content_url=site_content_url) + self.rest_api_base = self