Getting Started with the TABLEAU_REST_API library

This is the first post on how to actually use the tableau_rest_api Python library. The library handles most of the difficult things for you, but there are still some concepts that are worth getting a hang of. I highly recommend using an IDE, such as PyCharm, so you can easily see all the parameters of each of the methods.

Initial setup

Once the library is installed, you can import it into your scripts and get going. We’re going to create a TableauRestApi object to handle our requests, and a Logger object that can be shared between any TableauRestApi objects we create. Except for the initial sign-in action, all REST API calls are made when logged in to a particular site, so if you are doing actions across multiple sites, you’ll have to create a new TableauRestApi object for each site. You can log them all in the same file by passing the same Logger object, or pass separate ones to keep your logs separated.

We do a full import of ‘tableau_rest_api.tableau_rest_api’ because there are also some Exception classes we may want to reference when we do exception handling later. urllib2 is imported for its Exceptions as well. There are some actions that shouldn’t be done too quickly, so the standard ‘time’ package is also convenient to have available for its sleep function.

from tableau_rest_api.tableau_rest_api import *
import urllib2
import time
server = 'http://127.0.0.1'
username = 'my_username'
password = 'my_password'
tab_srv = TableauRestApi(server, username, password, site_content_url='default')
logger = Logger('rest_test.log')
tab_srv.enable_logging(logger)
tab_srv.signin()

LUIDs

As mentioned in a previous post on the REST API, all content / objects on the Tableau Server are referenced via local unique IDs (LUIDs), which are all alphanumeric codes that never change for that particular object. The library has many methods built in for getting the specific LUID of an object, and ‘create’ and ‘publish’ methods will return the LUID of the newly created/published content.

For example, let’s say we want to create a new user and then add them to an existing group called “Sandbox Users”.

try:
    group_luid = tab_srv.query_project_luid_by_name('Sandbox Users')
    # add_user actually adds and updates everything about the user
    # add_user(username, fullname, site_role='Unlicensed', password=None, email=None):
    new_user_luid = new_user_luid = tab_srv_3.add_user('newuser1', 'New User 1',
                                                       'Interactor', 'password', 'newuser1@nowhere.com')
    # Alternatively, you could do add_user_by_username(self, username, site_role='Unlicensed')
    # This does not define any of the additional properties
    # The methods that have plurals (like add_users) can take a single string or a list of luids
    tab_srv.add_users_to_group_by_luid(new_user_luid, group_luid)
except NoMatchFoundException as e:
    print e.msg

You’ll notice some of the style of the library methods — they are verbose and specify what type of input they are looking for: ‘_by_luid’ ends almost all of the ‘baseline’ methods that directly do actions, while ‘by_name’ methods do look ups to find a match. ‘by_name’ methods also can throw NoMatchFoundException class Exceptions, and then you can decide what to do from there.

Iterating through lists

The ‘query’ methods that end in a plural, like ‘query_groups()’ and ‘query_projects()’ return iterable lxml objects based on every item that came back. The REST API actually returns paginated results when the lists get large enough, but the library paginates through for you automatically and combines them into a single lxml object. You can then do any of the regular ElementTree or lxml methods to work with these objects, for example .xpath() for very specific querying.

There is also a static method of the TableauRestApi class which takes any of these lxml objects and converts it to a standard Python dict using { name : luid } as a pattern. This gives you an easily iterable dict that you can loop through to do mass changes.

groups = tab_srv.query_groups()
groups_dict = tab_srv.convert_xml_list_to_name_id_dict(groups)
for group in groups_dict:
    print "Group named {} is LUID {}".format(group, groups_dict.get(group))

tableau_rest_api Module for Python v1.0 released!

I’m excited to announce the v1.0 release of the tableau_rest_api module for Python 2.7. This is as far as I know the first publicly available full implementation of the Tableau Server 9.0 REST API. Every method implemented in the API reference docs is implemented, and all except for the AD sync methods are fully tested (this testing is occurring soon). The only requirement outside of the standard Python libraries is lxml, which is not being used to it’s fullest capacities but will be in later releases. At minimum, you get full XPath querying capabilities when working with the larger lists returned by some queries.

Available now on PyPI

https://pypi.python.org/pypi/tableau_rest_api

with source code hosted, as always on my GitHub.

Many more blog posts to come explaining how to use the library, and the design decisions that went into its current form.

I expect many bug fixes and additional features to come shortly as people begin using it.

Tool to Convert ArcGIS Shapefiles into Tableau Polygons

The Information Lab‘s Craig Bloodworth uploaded a fantastic tool to the free Alteryx Analytic Gallery that will convert any ZIP file containing ArcGIS SHP file information into Tableau polygons, and export that in either CSV or TDE.

All you need to do is sign up with Alteryx for free and then have a ZIP file to upload. For those new to all this, SHP or Shapefiles actually are composed of several files, so they are distributed in ZIP format to hold them all together.

https://gallery.alteryx.com/#!app/Tableau-Shapefile-to-Polygon-Converter/5296f89120aaf905b8e7fb48

These are the same techniques used by the folks over at http://tableaumapping.bi/ to build their shape sets, so you might find a useful one already converted over there.

URL Filters Work on both Original Field Name or Renamed Name

From embedded views to using tabcmd for unique exports, Tableau’s ability to filter using URL parameters is a fantastic way to programmatically drive customized vizes. Most of the use cases are covered in the KB article:

http://kb.tableau.com/articles/knowledgebase/view-filters-url

One thing that isn’t mentioned is that you can filter on both the original field name as Tableau brought it in from the original data source, or using the name you renamed it to. For example, I have renamed my “Region” field in the Super Store dataset from ‘Region’ to ‘Region - Renamed’. Both of these work when using URL filters:

…Sheet1?Region=East

and

…Sheet1?Region%20-%20Renamed=East

result in the same filtering. Just remember in both cases to delete the portion after the ‘:’ in 9 or the ‘#’ in the 8 series.

Creating Custom Polygons By Drawing Directly on a Google Map

There are times when you need a custom shapes because states or cities just aren’t cutting it, but you don’t have any definitions of the shapes. All you need are the longitude and latitude but how do you get them? I’ve built a tool that lets you draw directly onto a Google map and then generates the correct CSV output to be used as custom polygons in Tableau.

You can find the HTML file here on GitHub, simply download and save to a directory on your computer:

https://github.com/bryantbhowell/tableau-map-pack/blob/master/draw_tableau_polygons_on_google_maps.html

How does it work? When the Google map loads up, find the place you want to draw on and simply click on the point you want to start. A line will appear to draw the next place.

polygon on map

When you touch the line back to the starting point, a box will pop up and ask your what you want to name the polygon. This should be an identifier that matches up with the data you want to blend in later.

naming a polygon

Once you’ve created all the polygons you want, there is an “Open CSV Results” button

open results

which will pop open a dialog box with the CSV output. Copy this CSV into a text file and save, or paste it directly into Tableau. Now you have the data for your polygons.

csv to copy

You set these up in the exact same way as the CSV output from tool to draw on the background images, only you don’t need to do the background image part, you just use the Latitude and Longitude included in the CSV data. You can see those instructions at the end of this blog post

Implementing the Tableau 9.0 REST API — Part 3 — XML Parsing

XML

The “eXtensible Markup Language” is the basis for all messages in the Tableau REST API. Both your requests to and your responses from the Tableau Server will be made in UTF-8 encoded XML. In general, the Tableau REST API schema keeps all information in XML attributes, so there is no need to constantly determine if the info is in an attribute or in the text between the tags. However, there are two parts of the XML standard that are worth understanding and making sure you have facilities for.

XPath

XPath is the query language for the Document Object Model used in XML (and HTML). You bought Tableau so you wouldn’t have to write queries anymore, right! XML documents are not relational tables, but tree-models, and XPath was specifically designed to query this type of data structure effectively, making it very useful for finding exactly what you want in an XML document, regardless of where it is located or how many results there are. On the other hand, XPath bears zero resemblance to SQL, so you’ll have to learn to read and think in document trees with a whole new syntax.

XPath may not look familiar but it lets you answer some very interesting questions.

For example, when you query Workbook Permissions, there are a number of tags that have nested tags inside.

<granteeCapabilities>
<group id="group-id" />
<capabilities>
<capability name="capability" mode="capability-mode" />
</capabilities>
</granteeCapabilities>

The following XPath query will bring back matches all of the capabilities with a given name attribute that fall under the tag that has tag with the given group_luid

//t:granteeCapabilities[t:group[@id=’$group_luid’]]/t:capabilities/t:capability[@name=’$capability_name’]

We will see more examples of XPath as we work through more of the library. You’ll also notice the “t:” prefix used in the XPath query. This is a short alias for the Tableau REST API XML Namespace, and it is essential.

Namespaces

XML has a concept called Namespaces, and the Tableau REST API definitely has a declared namespace. In the PHP library, we register ‘t’ as an alias for the Tableau namespace so that later we can use the short letter before returning any XML response. You have to prefix every element you search for in an XPath query with the namespace or its alias, or it will not parse correctly and will return nothing.

public function getResponse(){
if($this->response_type == 'xml' && $this->simple_xml_object !== null){
// Register a namespace alias for xpath queries
$this->simple_xml_object->registerXPathNamespace('t', 'http://tableausoftware.com/api');
return $this->simple_xml_object;
}
else {
return $this->raw_response;
}
}

http://www.w3schools.com/xml/xml_namespaces.asp

XML Parsing Library

You’ll need to decide on a code library for parsing and working with XML. None of the XML structures are complex, but you’ll want something with facilities for handling namespaces, XPath, and easy iteration.

In the two example libraries,I’ve chosen SimpleXML( http://php.net/manual/en/book.simplexml.php ) in PHP and lxml ( http://lxml.de/ ) in Python. Both provide easy iteration using their language’s native facilities and also full XPath querying. ElementTree in Python does not have a complete set of XPath abilities, or as easy of a syntax for dealing with namespaces. You will see them both used as we dive into the libraries..

Implementing the Tableau 9.0 REST API — Part 2 — Tokens, Sign-In and LUIDs

Tokens

One distinct feature of the Tableau 9.0 REST API is that you must be logged in to a site on the Tableau Server to perform any action other than sign-in or sign-out. When you sign-in successfully to a site, you receive a token, which then must be passed as a header in the format

X-tableau-auth: token

in every subsequent HTTP request. So our first step needs to be signing in and grabbing the token for reuse.

Sign-in

What do you need to sign-in? At a basic level:

  • server location
  • username
  • password
  • site name: This is the “ContentUrl” i.e. the name as it appears in the browser URL

http://onlinehelp.tableau.com/current/api/rest_api/en-us/help.htm#REST/rest_api_ref.htm#Sign_In%3FTocPath%3DAPI%2520Reference|_____51

This is the XML request we need to make to log in:

<tsRequest>
<credentials name="username" password="password" >
<site contentUrl="site-name" />
</credentials>
</tsRequest>

And then we will send this as an HTTP POST (remember Part 1?) to the following URI:

https://$servername/api/2.0/auth/signin

If successful, we’ll receive back an XML response with two essential pieces of information

  • login token
  • site id / site luid
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<tsResponse version-and-namespace-settings>
<credentials token="12ab34cd56ef78ab90cd12ef34ab56cd">
<site id="9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
contentUrl="MarketingSite"/>
</credentials>
</tsResponse>
</pre>

You can see both of these items in the XML response. To do anything useful with them, we’re going to have to parse the XML to pull out the info we need. Before we touch on that, let’s talk about the format of the ‘id’ attribute and what an LUID is.

LUIDS

The ‘id’ attribute of the <site> tag is not the text of the site name (that is referred to as the ContentUrl). It isn’t really human readable at all. Most referencing in the Tableau REST API is done via these “locally unique identifiers” (LUIDs), which are 32-character hexadecimal strings that always identify a given resource. This is useful because many things can be changed about an object in Tableau Server — you can change a group’s name, or the full name of a user, etc. Most of the code library is devoted to finding and translating names into LUIDs so they can be referenced in the API calls.

Anywhere you see the “id” property in the REST API XML or a URI description, it is an LUID and not a name, even if the name never changes. From the sign-on process then, we want to grab the LUID of the site rather than the ContentUrl, because we will reuse it with every subsequent call, because the format for every call that affects something on a site must begin with the site luid, like:

https://$servername/api/2.0/sites/$side_luid/

http://onlinehelp.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_luid.htm