Skip to content

Commit

Permalink
Incrementing to 4.5.3, added method to adjust table identifiers where…
Browse files Browse the repository at this point in the history
… db/schema name is included
  • Loading branch information
Bryant Howell committed Aug 3, 2018
1 parent 599eb06 commit f64a04a
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 13 deletions.
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -1142,6 +1142,10 @@ You can access and set all of the relevant properties for a connection, using th

`TableauConnection.authentication`

If you are changing the dbname/schema on certain datasource types (Oracle and Teradata for sure, but possibly others), Tableau saves a reference to the database/schema name in the table name identifier as well. This attribute is actually stored in the relations tags in the datasource object directly (above the level of the connection), so you'll want to also call the following method:

`TableauDatasource.update_tables_with_new_database_or_schema(original_db_or_schema, new_db_or_schema)`

When you set using these properties, the connection XML will be changed when the save method is called on the TableauDatasource object.

ex.
Expand All @@ -1150,6 +1154,7 @@ ex.
dses = twb.tableau_document.datasources
for ds in dses:
if ds.published is not True: # See next section on why you should check for published datasources
ds.update_tables_with_new_database_or_schema(u'test_db, u'production_db') # For systems where db/schema is referenced in the table identifier
for conn in ds.connections:
if conn.dbname == u'test_db':
conn.dbname = u'production_db'
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -2,7 +2,7 @@

setup(
name='tableau_tools',
version='4.5.2',
version='4.5.3',
packages=['tableau_tools', 'tableau_tools.tableau_rest_api', 'tableau_tools.tableau_documents', 'tableau_tools.examples'],
url='https://github.com/bryantbhowell/tableau_tools',
license='',
Expand Down
16 changes: 16 additions & 0 deletions tableau_documents/tableau_datasource.py
Expand Up @@ -48,6 +48,7 @@ def __init__(self, datasource_xml=None, logger_obj=None, ds_version=None):
self.column_instances = []
self.main_table_relation = None
self.main_table_name = None
self.table_relations = None
self._connection_root = None
self._stored_proc_parameters_xml = None

Expand Down Expand Up @@ -224,6 +225,19 @@ def published_ds_content_url(self, new_content_url):
self.repository_location.attrib[u'id'] = new_content_url
self.connections[0].dbname = new_content_url

# It seems some databases like Oracle and Teradata need this as well to swap a database
def update_tables_with_new_database_or_schema(self, original_db_or_schema, new_db_or_schema):
"""
:type original_db_or_schema: unicode
:type new_db_or_schema: unicode
:return:
"""
for relation in self.table_relations:
if relation.get(u'type') == u"table":
relation.set(u'table', relation.get(u'table').replace(u"[{}]".format(original_db_or_schema),
u"[{}]".format(new_db_or_schema)))


@staticmethod
def create_new_datasource_xml(version):
# nsmap = {u"user": u'http://www.tableausoftware.com/xml/user'}
Expand Down Expand Up @@ -542,6 +556,7 @@ def read_existing_relations(self):
relation_type = self.relation_xml_obj.get(u'type')
if relation_type != u'join':
self.main_table_relation = self.relation_xml_obj
self.table_relations = [self.relation_xml_obj, ]

else:
table_relations = self.relation_xml_obj.findall(u'.//relation', self.ns_map)
Expand All @@ -552,6 +567,7 @@ def read_existing_relations(self):
if t.get(u'type') != u'join':
final_table_relations.append(t)
self.main_table_relation = final_table_relations[0]
self.table_relations = final_table_relations

# Read any parameters that a stored-proc might have
if self.main_table_relation.get(u'type') == u'stored-proc':
Expand Down
4 changes: 2 additions & 2 deletions tableau_rest_api/permissions.py
Expand Up @@ -210,10 +210,10 @@ def set_capabilities_to_match_role(self, role):
self.log_debug(u"Setting to role {} with capabilities {}".format(role, unicode(role_capabilities)))
if u"all" in role_capabilities:
if role_capabilities[u"all"] == u'Allow':
self.log_debug(u"Setting all capabilites to Allow")
self.log_debug(u"Setting all capabilities to Allow")
self.set_all_to_allow()
elif role_capabilities[u"all"] == u'Deny':
self.log_debug(u"Setting all capabilites to Deny")
self.log_debug(u"Setting all capabilities to Deny")
self.set_all_to_deny()
for cap in role_capabilities:
# Skip the all command, we handled it at the beginning
Expand Down
6 changes: 3 additions & 3 deletions tableau_rest_api/published_content.py
Expand Up @@ -185,8 +185,8 @@ def are_capabilities_obj_dicts_identical(new_obj_dict, dest_obj_dict):
if new_obj_dict.viewkeys() == dest_obj_dict.viewkeys():
for k in new_obj_dict:
if new_obj_dict[k] != dest_obj_dict[k]:
return True
return False
return False
return True
else:
return False

Expand Down Expand Up @@ -304,7 +304,7 @@ def set_permissions_by_permissions_obj_list(self, new_permissions_obj_list):
if need_to_change is False:
self.log(u'No changes necessary, skipping update for quicker performance')
self.end_log_block()
return True
continue
# Check if all capabilities are set to Unspecified, and ignore
specified_cap_count = 0
caps = new_permissions_obj.get_capabilities_dict()
Expand Down
6 changes: 3 additions & 3 deletions tableau_rest_api/tableau_rest_api_connection.py
Expand Up @@ -146,7 +146,7 @@ def __build_connection_update_xml(new_server_address=None, new_server_port=None,
return tsr

#
# Factory methods for PublishedContent and GranteeCapabilities objects
# Factory methods for PublishedContent and Permissions objects
#
def get_published_project_object(self, project_name_or_luid, project_xml_obj=None):
"""
Expand All @@ -170,7 +170,7 @@ def get_published_workbook_object(self, workbook_name_or_luid, project_name_or_l
if self.is_luid(workbook_name_or_luid):
luid = workbook_name_or_luid
else:
luid = self.query_datasource_luid(workbook_name_or_luid, project_name_or_luid)
luid = self.query_workbook_luid(workbook_name_or_luid, project_name_or_luid)
wb_obj = Workbook(luid, self, tableau_server_version=self.version, default=False, logger_obj=self.logger)
return wb_obj

Expand Down Expand Up @@ -637,7 +637,7 @@ def query_project(self, project_name_or_luid):
luid = project_name_or_luid
else:
luid = self.query_project_luid(project_name_or_luid)
proj = self.get_published_project_object(luid, self.query_single_element_from_endpoint(u'project', project_name_or_luid))
proj = self.get_published_project_object(luid, self.query_single_element_from_endpoint(u'project', luid))

self.end_log_block()
return proj
Expand Down
18 changes: 14 additions & 4 deletions tableau_rest_api/tableau_rest_api_connection_28.py
Expand Up @@ -224,8 +224,8 @@ def save_view_pdf(self, wb_name_or_luid, view_name_or_luid, filename_no_extensio
view_luid = self.query_workbook_view_luid(wb_name_or_luid, view_name=view_name_or_luid,
p_name_or_luid=proj_name_or_luid)
try:
if filename_no_extension.find('.pdf') == -1:
filename_no_extension += '.pdf'
if filename_no_extension.find(u'.pdf') == -1:
filename_no_extension += u'.pdf'
save_file = open(filename_no_extension, 'wb')
if view_filter_map is not None:
final_filter_map = {}
Expand All @@ -252,12 +252,14 @@ def save_view_pdf(self, wb_name_or_luid, view_name_or_luid, filename_no_extensio
self.end_log_block()
raise

def query_view_data(self, wb_name_or_luid, view_name_or_luid, filename_no_extension=None, proj_name_or_luid=None):
def save_view_data_as_csv(self, wb_name_or_luid, view_name_or_luid, filename_no_extension=None,
proj_name_or_luid=None, view_filter_map=None):
"""
:type wb_name_or_luid: unicode
:type view_name_or_luid: unicode
:type proj_name_or_luid: unicode
:type filename_no_extension: unicode
:type view_filter_map: dict
:rtype:
"""
self.start_log_block()
Expand All @@ -270,8 +272,16 @@ def query_view_data(self, wb_name_or_luid, view_name_or_luid, filename_no_extens
view_luid = self.query_workbook_view_luid(wb_name_or_luid, view_name=view_name_or_luid,
p_name_or_luid=proj_name_or_luid)
try:
if view_filter_map is not None:
final_filter_map = {}
for key in view_filter_map:
new_key = u"vf_{}".format(key)
final_filter_map[new_key] = view_filter_map[key]

url = self.build_api_url(u"views/{}/data".format(view_luid))
additional_url_params = u"?" + urllib.urlencode(final_filter_map)
else:
additional_url_params = u""
url = self.build_api_url(u"views/{}/data{}".format(view_luid, additional_url_params))
data = self.send_binary_get_request(url)
if filename_no_extension is not None:
if filename_no_extension.find('.csv') == -1:
Expand Down

0 comments on commit f64a04a

Please sign in to comment.