This post coincides with v1.3.0 of the tableau_rest_api library, which you should promptly be upgrading to for all the excellent functionality it provides
As shown previously, it’s not tremendously difficult to change the database/schema name (or port) in the XML of a TWB or TDS file — easy enough that we were able to add programmatic facilities for it to the tableau_rest_api library. Live connections aren’t the only side of Tableau though; extracts are an essential aspect of the Tableau experience. When we want to publish extracts, we end up using the Packaged File types – TWBX and TDSX (you may not be saving your stand alone extracts as a TDSX, but you should — a TDE file by itself stores no information about the datasource from whence it sprung, while a TDSX holds both the originating TDS and the TDE.
Packaged files are just plain ZIP files with a bit of structure (try it — you can unzip them, or ZIP up a TDS and rename the .zip to .tdsx and it will work). The main XML file (TWB or TDS) lives in the lowest level, and then data and other files exist in sub-directories. These sub-directories are not always the same, so it’s hard to generically say what will be inside. Since all we’re worried about is changing the XML file, we’ll just copy whatever we find in the sub-directories as is and hope for the best [this is a fundamental strategy in the rest_api_library : edit the least necessary pieces and copy the rest].
Using the TableauPackagedFIle class
Given the previous information about the packaged workbooks, there is a new class starting in tableau_rest_api v1.3.0 called TableauPackagedFile which can be instantiated from a file-like object of a ZIP file. It actually can determine whether it is a TDSX or a TWBX based on the files packaged inside, which can be checked via TableauPackagedFile.get_type(). The two other essential methods are get_tableau_object() which will return either a TableauDatasource object or a TableauWorkbook object, depending on if you are dealing with a TWBX or TDS. Any manipulation you do in the objects to their internal TableauConnection object(s) will be saved when you do a save_new_packaged_file(new_filename_no_extension) .
But wait, there is more! You can pass a TableauPackagedFile object directly into the publish_ methods in the place of the filename, and the new resulting file will be brought down temporarily and then published up to the server.
from tableau_rest_api.tableau_rest_api import * import urllib2 username = '' password = '' server = 'http://127.0.0.1' logger = Logger('switch.log') # Modifying the TDSX tdsx_filename = "somefile.tdsx" fh = open(tdsx_filename, 'rb') pfile = TableauPackagedFile(fh, logger) if pfile.get_type() == 'tdsx': ds = pfile.get_tableau_object() ds.connection.set_dbname('Super Store') pfile.save_new_packaged_file('new_file') fh.close() # Publishing the modified TDSX default = TableauRestApi(server, username, password) default.signin() proj_luid = default.query_project_luid_by_name('default') default.publish_datasource('new_file.tdsx', 'Changed DS 2', proj_luid, True) # Modifying a TWBX twbx_filename = "some workbook.twbx" fh = open(twbx_filename, 'rb') pfile = TableauPackagedFile(fh, logger) if pfile.get_type() == 'twbx': wb = pfile.get_tableau_object() datasources = wb.get_datasources() for ds in datsources: ds.connection.set_dbname('New Database') fh.close() proj_luid = default.query_project_luid_by_name('default') default.publish_datasource(pfile, 'Changed TWBX', proj_luid, True)
How IT WOrks
The zipfile module in the standard Python library actually handles all the functionality we need. If you are re-implementing in another language, there’s not much going on. My algorithm is just to ignore anything that has a sub-directory as part of its filename, then look for the .twb or .tds. After that, I just copy all content from the sub-directories into the new file, as is.
The XML file gets represented as its appropriate class– since the library already has classes that represent the hierarchy, it just piggybacks on the existing capabilities to work with TDS and TWB files.