Monday, June 15, 2015

Make Dashboards' formatting consistent.

Has this ever happened to you?

In the course of normal events dashboards get built with a hodgepodge of styles.

For example, in the Workbook to the right the

  • default dashboard has not had any formatting applied, while the
  • formatted dashboard has been formatted.

It's sometimes desirable to have a consistent Dashboard look and feel without going through the tremendously tedious manual process of configuring them individually.

Tableau lacks the ability to enable this, either by setting the defaults you want, or by applying formatting to Dashboards in bulk.

But you can now do it, simply and easily.
There's a Tableau Tool for applying the formatting from a template dashboard to all of the dashboards in a set of workbooks.

Here's a dashboard with the formatting to apply to a selected set of Workbooks.

We'll see below how to make this happen–it's a pretty simple matter of running the appropriate Tableau Tool in the directory where the Workbooks are.

The tool will take the Template formatting and apply it to all the dashboards it's pointed at.

For the tool to work there are two important aspects to this Workbook:

  • The Workbook is named
    Template.twb
  • The Dashboard is also named
    Template.

Of course, if one wants to use another Workbook and Dashboard name, it's easy to reconfigure the tool to accommodate them.

The formatted Dashboards.

Here are the default and formatted dashboards re-formatted with the Template formatting.

Important points about the formatting:

  • Only the formatting shown above will be applied;
    there are other things one might wish to configure, but there are complications that go along with them.
  • All of the Template Dashboard's formatting will be applied, even if some part of it hasn't been configured;
    it's possible to implement finer-grained control of what formatting gets applied, but that gets complicated, beyond the scope of this initial formatting approach.

The tool: SetDashboardFormatAll.rb
is available on GitHub here, or can be copy/pasted from below.


  #  Copyright (C) 2014, 2015  Chris Gerrard
  #
  #  This program is free software: you can redistribute it and/or modify
  #  it under the terms of the GNU General Public License as published by
  #  the Free Software Foundation, either version 3 of the License, or
  #  (at your option) any later version.
  #
  #  This program is distributed in the hope that it will be useful,
  #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  #  GNU General Public License for more details.
  #
  #  You should have received a copy of the GNU General Public License
  #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  
  require 'twb'
  require 'nokogiri'
  require 'csv'
  
  $templateTwb  = 'Template.twb'
  $templateDash = 'Template'
  $twbAppend    = '_styled_'
  
  puts "\n\n"
  puts " Setting Workbook dashboard formatting, using the formatting"
  puts " from the #{$templateDash} dashboard"
  puts "   in the #{$templateTwb} workbook\n\n"
  
  $csv = CSV.open("TT-FormattedWorkbooks.csv", "w")
  $csv << ["Workbook","Dashboard"]
  
  def loadTemplate
    return 'Template.twb not found' unless File.file?('Template.twb')
    twb  = Twb::Workbook.new('Template.twb')
    dash = twb.dashboards['Template']
    return 'Template dashboard not found' if dash.nil?
    style = dash.node.at_xpath('./style')
    return '  ERROR - no style available from Template dashboard.' if style.nil?
    puts "   Dashboard styling:"
    styleRules = style.xpath('./style-rule')
    if styleRules.empty?
      puts "\n\t  Template dashboard formatting is default style."
    else
      styleRules.each do |rule|
        puts "\n\t Element: #{rule['element']}"
        formats = rule.xpath('./format')
        formats.each do |f|
          puts sprintf("\t -- %-16s : %s \n", f['attr'], f['value'])
        end
      end
    end
    puts "\n"
    return style
  end
  
  def processTwbs
    path = if ARGV.empty? then '*.twb' else ARGV[0] end
    puts " Looking for TWBs using: #{ARGV[0]} \n\n"
    Dir.glob(path) do |fname|
      setTwbStyle(fname) unless fname.eql?($templateTwb) || !fname.end_with?('.twb')
    end
  end
  
  def setTwbStyle fname
    return if fname.eql?($templateTwb) || fname.include?($twbAppend + '.twb')
    twb = Twb::Workbook.new(fname)
    dashes = twb.dashboards.values
    puts sprintf("\t%3d in: '%s' ", dashes.length, fname)
    return if dashes.empty?
    dashes.each do |dash|
      node  = dash.node
      style = node.at_xpath('./style')
      tmpStyle = $templateStyle.clone
      style.replace(tmpStyle)
      $csv << [fname, dash.name]
    end
    twb.writeAppend($twbAppend)
  end
  
  $templateStyle = loadTemplate
  if $templateStyle.class == 'String'.class
    puts "\t #{$templateStyle}\n\n"
  else
    processTwbs
  end
  
  $csv.close unless $csv.nil?

The Tool in action
Here's the code being run.

In this case the execution command specifies that only the single Workbook ExampleDashboards.twb will have its Dashboard(s) formatted.

Upon startup, the tool looks for the Template Workbook and Dashboard and, assuming it finds them, prints out the formatting found there.

It then looks for Workbooks, either all of them, or those matching the single command line parameter. Those that it finds have any Dashboards they contain formatted to the Template configuration.

It then looks for Workbooks, either all of them, or those matching the single command line parameter. Those that it finds are listed with the number of their Dashboards, if any, and have their Dashboards formatted to the Template configuration.

By default, the Template-formatted dashboards are written to a copy of the original, with '._styled_' appended to the name. This is a precaution, ensuring that the original Workbook isn't harmed in the process. Adjusting the Tool to apply the formatting directly to the Workbook is a small change, easily made.


 ...$ ls -1 ExampleDashboards*.twb
 ExampleDashboards.twb

 ...$ ruby "{path to}\SetDashboardFormatAll.rb" ExampleDashboards.twb  


  Setting Workbook dashboard formatting, using the formatting
  from the Template dashboard
    in the Template.twb workbook

    Dashboard styling:

          Element: table
          -- background-color : #fae7c8

          Element: dash-title
          -- font-weight      : normal
          -- color            : #000000
          -- font-size        : 14
          -- background-color : #f0d9b6
          -- border-color     : #b40f1e
          -- border-style     : solid

          Element: dash-subtitle
          -- font-size        : 11
          -- font-weight      : normal
          -- color            : #b40f1e
          -- background-color : #d7d7d7

          Element: dash-text
          -- text-align       : center
          -- color            : #b40f1e
          -- background-color : #e1e8fa
          -- border-color     : #1b1b1b
          -- border-style     : solid
          -- font-family      : Comic Sans MS

  Looking for TWBs using: ExampleDashboards.twb

           2 in: 'ExampleDashboards.twb'

 ...$ ls -1 ExampleDashboards*.twb
 ExampleDashboards._styled_.twb
 ExampleDashboards.twb

 ...$

Friday, June 5, 2015

This changes everything - Autodocumenting Workbooks

Your Workbooks can document themselves, now that programmatically creating and injecting Dashboards that document Workbooks into them is a reality. And it's free.

Consider the Regional Sample Workbook. It has six separate vizzes, but one easily can't tell whether any of them is a Dashboard without visually inspecting it. Nor can we see which Worksheets a Dashboard includes without opening it, and there's no way to tell how all the Dashboards relate to all the Worksheets, or the Worksheets to the Data Sources they access.

Imagine, if you please, what it would look like if there was a way to have the Workbook -> Dashboards -> Worksheets -> Data Sources relationships teased out of the Workbook, rendered graphically, and then added back into the Workbook as a self-documenting Dashboard.

Would you like that? Would it be handy? Useful? Maybe? Until now, this sort of thing has been difficult, awkward, and manually intensive. If it was doable at all.

Not any longer. It's now simple and straightforward, with a minimum of fuss, for one workbook, or a whole boatload of them.

Here it is — the Regional Workbook, autodocumented,
with the map of its Dashboards, Worksheets, and Data Sources automatically generated and injected into it.

Adding documentation to Workbooks, where it really matters, has always been a manual, laborious process, with so much friction that it hasn't been feasible at scale. No more. Tableau Tools' now has the ability to inject dashboards into existing dashboards automatically. Coupled with its existing abilities to generate useful content about Workbooks, this opens entire new horizons for enriching Workbooks with surprisingly little effort.

How the magic happens.

The simple version: run a simple Ruby script in a directory containing Workbooks to be documented, and presto! the Workbook(s) are documented with the desired content; in this case the D->W->DS maps for each.

Run this code in a directory containing Workbooks.
# Copyright (C) 2014, 2015 Chris Gerrard # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . require 'twb' puts "\n\n\t Documenting Workbooks with Tableau Tools" puts "\n\t Adding Dashboard -> Worksheet -> Data Source graphs" puts "\n\t https://github.com/ChrisGerrard/Tableau-Tools" puts "\n" path = if ARGV.empty? then '*.twb' else ARGV[0] end puts "\n\t Files matching: '#{path}'" Dir.glob(path) do |twb| puts "\t -- #{twb}" twb = Twb::Workbook.new(twb) dotBuilder = Twb::Util::TwbDashSheetDataDotBuilder.new(twb) dotFile = dotBuilder.dotFileName renderer = Twb::Util::DotFileRenderer.new imageFile = renderer.render(dotFile,'png') dash = Twb::DocDashboardImageVert.new dash.image=(imageFile) dash.title=('Dashboards, Worksheets, and Data Sources') twb.addDocDashboard(dash) twb.writeAppend('dot') end

The code is available on GitHub here.
Not much to it, is there?
Tableau Tools does the work, needing only a little glue code to compose the specific functionality.

Here's the code running in a directory containing Tableau Sample Workbooks.

As the code runs it picks up all the *.twb files in the directory and processes each in turn.


  Dir.glob(path) do |twb|
  puts "\t -- #{twb}"
  twb        = Twb::Workbook.new(twb)

Each file has its D->W->DS map built with:


  dotBuilder = Twb::Util::TwbDashSheetDataDotBuilder.new(twb)
  dotFile    = dotBuilder.dotFileName
  renderer   = Twb::Util::DotFileRenderer.new

The D->W->DS map image file is added to a Dashboard which is then injected into the Workbook with:


  dash       = Twb::DocDashboardImageVert.new
  dash.image=(imageFile)
  dash.title=('Dashboards, Worksheets, and Data Sources')
  twb.addDocDashboard(dash)

The Workbook is written out, as a copy with '.dot' added to its name with:


  twb.writeAppend('dot')

There's no technical reason to create a new copy of the Workbook, and in a production environment the Workbooks are mostly documented in place, i.e. keeping their own names.

Dependencies

Ruby 1.9.3
is preferred, almost exclusively because the Nokogiri gem that's required under the covers works seamlessly with 1.9.3 (on Windows), and not so seamlessly with Ruby 2.x. See the Nokogiri section below for more information.


The Twb gem
is required, and is declared as such via "require 'twb'", the first executable (non-comment) line.
The gem is installable via "...> gem install twb", which assumes that "gem " is installed, which should have happened when Ruby was installed.


 ... Tableau Sample Workbooks> gem install twb
 Successfully installed twb-0.3.2
 1 gem installed
 Installing ri documentation for twb-0.3.2...
 Installing RDoc documentation for twb-0.3.2...

 ... Tableau Sample Workbooks> gem list twb

 *** LOCAL GEMS ***

 twb (0.3.2)

 ... Tableau Sample Workbooks> 

(post-publishing) note:
The correct gem version is 0.3.2 (as of this writing) – there was a glitch in the gem publishing that I didn't catch at the time of the original post resulting in an obsolete version of it being installed.
Thanks to Philip, Vishwanath, and Matthew for reporting this.


The Nokogiri gem
Nokogiri is an XML and HTML parsing gem that's used by the Twb gem. If it's not installed the 'require' statement in Twb will fail.
Nokogiri is installable thus:


 ... Tableau Sample Workbooks> gem install nokogiri
 Fetching: mini_portile-0.6.2.gem (100%)
 Fetching: nokogiri-1.6.6.2-x86-mingw32.gem (100%)
 Nokogiri is built with the packaged libraries: libxml2-2.9.2, libxslt-1.1.28, zlib-1.2.8, libiconv-1.14.
 Successfully installed mini_portile-0.6.2
 Successfully installed nokogiri-1.6.6.2-x86-mingw32
 2 gems installed
 Installing ri documentation for mini_portile-0.6.2...
 Installing ri documentation for nokogiri-1.6.6.2-x86-mingw32...
 Installing RDoc documentation for mini_portile-0.6.2...
 Installing RDoc documentation for nokogiri-1.6.6.2-x86-mingw32...

 ... Tableau Sample Workbooks> 

note:
Nokogiri is sensitive to the Ruby version being used.
I'm using Ruby 1.9.3, mostly because: it's stable; suitable for my purposes; inertia; and Nokogiri didn't work with 2.x Ruby in the limited testing I did.
There's some documentation online about Nokogiri and Ruby 2.x but I've not had the time to puzzle out how to get them to play nice together.


Graphviz
The diagrams are created using Graphviz - open source graphing software, which can be downloaded and installed from here.

Graphviz location In order for the graphs to be rendered, Graphviz needs to be available for use by the Twb Twb::Util::DotFileRenderer object, which assumes the default Graphviz installation: 'C:\tech\graphviz\Graphviz2.38\bin\dot.exe'.
'dot.exe' is the Graphviz program that renders this particular graph type—there are many other types, each with their specific rendering program.

If Graphviz is installed into another directory, it can be communicated to the Twb rendered by adding the following line to the code (new line bold):


  renderer   = Twb::Util::DotFileRenderer.new
  renderer.gvDotLocation=('dot.exe location') 


To infinity, and beyond.

With the Workbook injecting nut cracked, there's no end to the things that can be done. Whatever can be thought up can be created and added to your Workbooks.

But wait, there's more.

Workbook autodocumenting relies upon Tableau Tools' ability to modify and write Workbooks. This core feature makes it possible to compose functionality to accomplish pretty much anything you could want to do with and to your Workbooks. The door is open, the future beckons, and it going to be a fun ride.

About Tableau Tools

Tableau Tools is open source software for interrogating and manipulating Tableau Workbooks and other artifacts. It has two main parts as of this writing:

  • The TWB Ruby gem, available on Github at: https://github.com/ChrisGerrard/TWB, is the core element. It models Workbooks and their major components, allowing for easy access to and control of them. Based on Nokogiri, it provides the opportunity to employ Nokogiri when access and manipulation of deeper components for specific uses is needed.
  • The Tableau Tools project, also available on GitHub: https://github.com/ChrisGerrard/Tableau-Tools contains tools for Tableau assessment and manipulation, including all manner of useful tools that can be composed into richly functional suites with minimal fuss and bother.