library(twbparser)
ok <- FALSE
twb_path <- system.file("extdata", "test_for_wenjie.twb", package = "twbparser")
if (nzchar(twb_path) && file.exists(twb_path)) {
parser <- TwbParser$new(twb_path)
ok <- TRUE
} else {
cat("> Demo .twb not found in installed package. Skipping executable examples.\n")
}
#> TWB loaded: test_for_wenjie.twb
#> TWB parsed and readytwbparser parses Tableau .twb and
.twbx workbooks and exposes datasources, relationships,
joins, fields, calculated fields, and TWBX assets. It also provides
page-centric insights — dashboards, worksheets, stories, their
composition, filter positions, chart types, and colors/palettes — as
well as per-worksheet shelf/filter/axis/sort details and per-dashboard
zone layout and actions. This vignette demonstrates common use
cases.
parser$summary
#> TWB PARSER SUMMARY
#> ------------------
#> File: test_for_wenjie.twb
#> Datasources: 2
#> Parameters: 0
#> Worksheets: 1
#> Dashboards: 0
#> Stories: 0
#> Relationships: 1
#> Calculated fields: 1
#> Raw fields: 55
#> Worksheet filters: 0
#> Dashboard filters: 0
#> Custom SQL blocks: 0
#> Initial SQL blocks: 0
#>
#> Datasources
#> -----------
#> # A tibble: 2 × 4
#> datasource_name connection_type field_count
#> <chr> <chr> <int>
#> 1 Municipal_Boundaries_of_NJ (1) ogrdirect 0
#> 2 federated.0grgaor1pd01yy1f0yr380of1ags federated 110
#> location
#> <chr>
#> 1 Shapefile: Municipal_Boundaries_of_NJ (1).zip
#> 2 Federated: <unknown>
#>
#> Pages
#> -----
#> # A tibble: 1 × 6
#> page_type name mark_types n_filters n_legends n_parameter_controls
#> <chr> <chr> <chr> <int> <int> <int>
#> 1 worksheet Sheet 1 "" 0 0 0
#>
#> Worksheet Shelves
#> -----------------
#> # A tibble: 1 × 3
#> sheet shelves
#> <chr> <chr>
#> 1 Sheet 1 color, cols, geometry, lod, rows
#> fields
#> <chr>
#> 1 Calculation_2139209847776120832, Longitude (generated), Geometry, counts, Lat…
#>
#> Worksheet Filters
#> -----------------
#> No worksheet filters found.
#>
#> Dashboard Filters
#> -----------------
#> No dashboard filters found.
#>
#> Calculated Fields
#> -----------------
#> 1. no data
#> Datasource: federated.0grgaor1pd01yy1f0yr380of1ags
#> Type: string dimension
#> Calculation: raw
#> Table calc: FALSE
#> Dependencies: 1
#> Formula:
#> if ISNULL([counts])
#> THEN "missing"
#> ELSE "available"
#> END
#>
#>
#> SQL
#> ---
#> No custom or initial SQL found.
parser$overview
#> # A tibble: 1 × 9
#> file datasources parameters relationships calculated_fields raw_fields
#> <chr> <int> <int> <int> <int> <int>
#> 1 test_for_we… 2 0 1 1 55
#> # ℹ 3 more variables: inferred_relationships <int>, dashboards <int>,
#> # total_filters <int>datasources <- parser$get_datasources()
parameters <- parser$get_parameters()
print(head(datasources))
#> # A tibble: 2 × 10
#> datasource primary_table connection_id connection_caption connection_class
#> <chr> <chr> <chr> <chr> <chr>
#> 1 Municipal_Bou… [Municipal_B… ogrdirect.07… Municipal_Boundar… ogrdirect
#> 2 Sheet1 [Sheet1$] excel-direct… test_county excel-direct
#> # ℹ 5 more variables: connection_target <chr>, datasource_name <chr>,
#> # field_count <int>, connection_type <chr>, location <chr>
print(head(parameters))
#> # A tibble: 0 × 0Parameters are excluded by default from calculated fields; opt-in via
include_parameters = TRUE.
head(parser$get_fields())
#> # A tibble: 6 × 10
#> datasource name caption datatype role semantic_role table table_clean
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 federated.0grgao… OBJE… <NA> integer <NA> <NA> <NA> <NA>
#> 2 federated.0grgao… MUN <NA> string <NA> <NA> <NA> <NA>
#> 3 federated.0grgao… COUN… <NA> string <NA> <NA> <NA> <NA>
#> 4 federated.0grgao… MUN_… <NA> string <NA> <NA> <NA> <NA>
#> 5 federated.0grgao… MUN_… <NA> string <NA> <NA> <NA> <NA>
#> 6 federated.0grgao… NAME <NA> string <NA> <NA> <NA> <NA>
#> # ℹ 2 more variables: field_clean <chr>, is_parameter <lgl>
head(parser$get_calculated_fields(pretty = TRUE, wrap = 120))
#> # A tibble: 1 × 9
#> datasource name datatype role is_table_calc calc_class formula_pretty
#> <chr> <chr> <chr> <chr> <lgl> <chr> <chr>
#> 1 federated.0grgao… no d… string dime… FALSE tableau "if ISNULL([c…
#> # ℹ 2 more variables: tableau_internal_name <chr>, table_clean <chr>List all pages and summarize each page
twb_pages(parser)
#> # A tibble: 1 × 2
#> page_type name
#> <chr> <chr>
#> 1 worksheet Sheet 1
twb_pages_summary(parser)
#> # A tibble: 1 × 6
#> page_type name mark_types n_filters n_legends n_parameter_controls
#> <chr> <chr> <chr> <int> <int> <int>
#> 1 worksheet Sheet 1 "" 0 0 0Inspect what a specific page contains
pg <- twb_pages(parser)
nm <- if (nrow(pg)) pg$name[[1]] else NA_character_
if (!is.na(nm)) {
parser$get_page_composition(nm)
}
#> # A tibble: 1 × 11
#> page_type page_name component_type zone_id target field presentation x
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <int>
#> 1 worksheet Sheet 1 mark_type <NA> <NA> <NA> <NA> NA
#> # ℹ 3 more variables: y <int>, w <int>, h <int>Filters and their positions across dashboards
twb_dashboard_filters(parser)
#> # A tibble: 0 × 9
#> # ℹ 9 variables: dashboard <chr>, zone_id <chr>, zone_type <chr>, field <chr>,
#> # presentation <chr>, x <int>, y <int>, w <int>, h <int>Chart (mark) types per worksheet and colors/palettes
Each of the four functions below accepts an optional
sheet argument to restrict output to a single
worksheet.
shelves <- twb_sheet_shelves(parser)
head(shelves)
#> # A tibble: 6 × 7
#> sheet shelf field_ref field_instance field_clean datasource aggregation
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Sheet 1 color [federated… none:Calculat… Calculatio… federated… <NA>
#> 2 Sheet 1 cols [federated… Longitude (ge… Longitude … federated… <NA>
#> 3 Sheet 1 geometry [federated… clct:Geometry… Geometry federated… <NA>
#> 4 Sheet 1 lod [federated… clct:Geometry… Geometry federated… <NA>
#> 5 Sheet 1 lod [federated… none:counts:qk counts federated… <NA>
#> 6 Sheet 1 rows [federated… Latitude (gen… Latitude (… federated… <NA>The shelf column distinguishes "rows",
"cols", "color", "size",
"label", "detail", and
"tooltip".
filters <- twb_sheet_filters(parser)
head(filters)
#> # A tibble: 0 × 9
#> # ℹ 9 variables: sheet <chr>, field_ref <chr>, field_clean <chr>,
#> # datasource <chr>, filter_class <chr>, include_mode <chr>, members <chr>,
#> # range_min <chr>, range_max <chr>Categorical filters include a comma-separated members
column; range filters populate range_min /
range_max.
spec <- twb_sheet_spec(parser, sheet = "Sheet 1")
spec
#> Sheet: Sheet 1
#> Mark type: map
#> Datasources: federated.0grgaor1pd01yy1f0yr380of1ags
#> Rows (1): Latitude (generated)
#> Cols (1): Longitude (generated)
#> Dimensions (5): Latitude (generated), Longitude (generated), Calculation_2139209847776120832, Geometry, counts
#> Measures: (none)
#> Encodings: color -> Calculation_2139209847776120832; geometry -> Geometry; lod -> Geometry; lod -> counts
#> Tooltips:none
#> Filters: 0 Sorts: 0 Axes: 0twb_sheet_spec() returns one spec per worksheet: the
mark type, the rows/columns shelves in order, the dimensions and
measures in play, every marks-card encoding, the tooltip configuration,
and the sheet’s filters, sorts, and axes — the full blueprint for
rebuilding the visualization in another tool.
parser$sheet_spec exposes the same specs as a property.
Three helpers answer the questions that come up when rebuilding a
workbook in another tool. They are demonstrated on the bundled
rebuild_kit.twb fixture, which contains chained
calculations, a circular pair, parameters, and deliberately unused
fields.
kit_ok <- FALSE
kit_path <- system.file("extdata", "rebuild_kit.twb", package = "twbparser")
if (nzchar(kit_path) && file.exists(kit_path)) {
kit <- TwbParser$new(kit_path)
kit_ok <- TRUE
}
#> TWB loaded: rebuild_kit.twb
#> TWB parsed and ready# Fields defined but never used anywhere: the safe-to-drop list
twb_unused_fields(kit)
#> # A tibble: 3 × 7
#> datasource field_type name tableau_internal_name datatype role is_hidden
#> <chr> <chr> <chr> <chr> <chr> <chr> <lgl>
#> 1 sales-data calculated Unused C… [Calculation_0003] real meas… NA
#> 2 sales-data parameter Unused P… [Parameter 2] string dime… NA
#> 3 sales-data raw Unused F… [Unused Field] string dime… FALSE# Calculations in creation order — "Adjusted Ratio" comes after "Profit Ratio";
# the Cycle A/B pair is flagged instead of silently misordered
twb_calc_build_order(kit)
#> Warning: Circular dependencies detected among calculated fields: Cycle A, Cycle
#> B. `build_order` is NA for these fields; break the cycle before rebuilding.
#> # A tibble: 5 × 8
#> build_order datasource name tableau_internal_name formula depends_on
#> <int> <chr> <chr> <chr> <chr> <chr>
#> 1 1 sales-data Profit Ratio [Calculation_0001] [Profi… <NA>
#> 2 2 sales-data Unused Calc [Calculation_0003] [Sales… <NA>
#> 3 3 sales-data Adjusted Ratio [Calculation_0002] [Profi… Profit Ra…
#> 4 NA sales-data Cycle A [Calculation_0004] [Cycle… Cycle B
#> 5 NA sales-data Cycle B [Calculation_0005] [Cycle… Cycle A
#> # ℹ 2 more variables: n_calc_deps <int>, is_cyclic <lgl># Where each parameter value flows: formulas, shelves, filters, dashboards
twb_parameter_usage(kit)
#> # A tibble: 2 × 6
#> parameter datasource datatype current_value context location
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Top N sales-data integer 5 filter Profit Detail
#> 2 Top N sales-data integer 5 formula Adjusted Ratiotwb_unused_fields() covers raw fields, calculated
fields, and parameters; twb_calc_build_order()
warns and marks build_order = NA for fields caught in a
dependency cycle; parameters with no usages are found via
twb_unused_fields() rather than appearing empty in
twb_parameter_usage().
charts <- twb_dashboard_charts(parser)
head(charts)
#> # A tibble: 0 × 18
#> # ℹ 18 variables: dashboard <chr>, sheet <chr>, mark_type <chr>,
#> # mark_source <chr>, rows <list>, cols <list>, dimensions <list>,
#> # measures <list>, tooltip_fields <list>, n_tooltip_fields <int>,
#> # has_tooltip <lgl>, n_filters <int>, datasources <list>, zone_id <chr>,
#> # x <int>, y <int>, w <int>, h <int>One row per worksheet placed on each dashboard: mark type, fields
(rows, cols, dimensions,
measures as list-columns), tooltip summary, and layout
position. (The bundled demo workbook has no dashboards, so this is empty
here.)
db_sheets <- twb_dashboard_sheets(parser)
head(db_sheets)
#> # A tibble: 0 × 7
#> # ℹ 7 variables: dashboard <chr>, sheet <chr>, zone_id <chr>, x <int>, y <int>,
#> # w <int>, h <int>x, y, w, h are
pixel coordinates within the dashboard canvas.
layout <- twb_dashboard_layout(parser)
head(layout)
#> # A tibble: 0 × 10
#> # ℹ 10 variables: dashboard <chr>, zone_id <chr>, parent_zone_id <chr>,
#> # component_type <chr>, target <chr>, layout_type <chr>, x <int>, y <int>,
#> # w <int>, h <int>parent_zone_id links child zones to their container;
root zones have NA. component_type is one of
"worksheet", "filter",
"container", "legend",
"parameter_control", "text",
"image", or "blank".
actions <- twb_dashboard_actions(parser)
head(actions)
#> # A tibble: 0 × 6
#> # ℹ 6 variables: action_name <chr>, action_type <chr>, source_sheets <chr>,
#> # target_sheet <chr>, run_on <chr>, url <chr>action_type is "filter",
"url", "highlight", or
"parameter". source_sheets is a
comma-separated list; url is populated for URL actions.
relations <- parser$get_relationships()
head(relations)
#> # A tibble: 1 × 8
#> relationship_type left_table right_table left_field operator right_field
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Relationship Sheet1 Municipal_Bounda… County = COUNTY
#> # ℹ 2 more variables: left_is_calc <lgl>, right_is_calc <lgl>For non-interactive use, parse_twb() parses the workbook
and writes a structured report to disk — a human-readable
report.txt, one CSV per key table, a plain-text replication
brief, and the field dependency graph as GraphML:
out <- parse_twb(parser$path,
output_dir = file.path(tempdir(), "twbparser-vignette"),
overwrite = TRUE, quiet = TRUE)
list.files(out)
#> [1] "calc_build_order.csv" "calculated_fields.csv"
#> [3] "custom_sql.csv" "dashboards.csv"
#> [5] "datasources.csv" "dependency_graph.graphml"
#> [7] "fields.csv" "joins.csv"
#> [9] "overview.csv" "pages.csv"
#> [11] "parameter_usage.csv" "parameters.csv"
#> [13] "relationships.csv" "replication_brief.txt"
#> [15] "report.txt" "sheet_specs.txt"
#> [17] "unused_fields.csv"This vignette overviewed how to use the twbparser
package for detailed inspection and extraction of Tableau workbook
internals to assist in analysis, replication, or integration
workflows.