Convert Tableau Prep Flow files to Python/pandas scripts
Project description
TFL Decompiler
Convert Tableau Prep Flow files (.tfl) to Python/pandas scripts.
Say goodbye to Tableau Prep's proprietary flows. Transform your data transformations into portable, version-controlled, scalable Python code.
Table of Contents
- Installation
- Quick Start
- Usage
- Example Output
- Supported Transformations
- Architecture
- Why Replace Tableau Prep?
- Contributing
- License
Installation
pip install tfl-decompiler
With optional database connectors:
# PostgreSQL
pip install "tfl-decompiler[postgresql]"
# MySQL
pip install "tfl-decompiler[mysql]"
# SAP HANA
pip install "tfl-decompiler[saphana]"
# All database connectors
pip install "tfl-decompiler[all]"
Requirements: Python 3.9+
Quick Start
tfl-decompile my_flow.tfl -o my_flow.py
python my_flow.py
Usage
Command Line
After installation, the tfl-decompile command is available on your PATH.
# Convert a flow to Python (save to file)
tfl-decompile my_flow.tfl -o my_flow.py
# Print generated code to stdout
tfl-decompile my_flow.tfl
# Analyze flow structure without generating code
tfl-decompile my_flow.tfl --analyze
# Batch convert all flows in a directory
for f in *.tfl; do tfl-decompile "$f" -o "${f%.tfl}.py"; done
You can also run it as a module:
python -m tfl_decompiler my_flow.tfl -o my_flow.py
Python API
from tfl_decompiler import decompile
# Generate Python code string
code = decompile("my_flow.tfl")
print(code)
# Generate and save to file
decompile("my_flow.tfl", output_path="my_flow.py")
# Inspect the flow structure
from tfl_decompiler import TFLExtractor
extractor = TFLExtractor("my_flow.tfl")
content = extractor.extract()
summary = extractor.get_flow_summary(content)
print(summary)
# Fine-grained control
from tfl_decompiler import FlowParser, CodeGenerator
parser = FlowParser(content)
graph = parser.build_graph()
generator = CodeGenerator(graph)
code = generator.generate()
Example Output
Given a Tableau Prep flow that reads a CSV, renames columns, filters rows, aggregates, and writes output:
#!/usr/bin/env python3
"""
Auto-generated from: sales_flow.tfl
Generated by: TFL Decompiler
"""
import pandas as pd
import numpy as np
import os
from pathlib import Path
def main(input_sales: str = "data/sales.csv", output_results: str = "output/results.csv"):
"""Execute the data transformation flow."""
# Step 1: Load data from CSV file
df_input_sales = pd.read_csv(input_sales)
# Step 2: Clean step (3 operations): rename, changetype, filter
df_clean_transform = df_input_sales.copy()
df_clean_transform = df_clean_transform.rename(columns={"old_name": "new_name"})
df_clean_transform["amount"] = df_clean_transform["amount"].astype(float)
df_clean_transform = df_clean_transform[df_clean_transform["status"] == "active"]
# Step 3: Aggregate by [product_category]
df_agg_summary = df_clean_transform.groupby("product_category").agg(
{"amount": "sum", "quantity": "mean"}
).reset_index()
# Step 4: Output to CSV
os.makedirs(Path(output_results).parent, exist_ok=True)
df_agg_summary.to_csv(output_results, index=False)
return df_agg_summary
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Decompiled Tableau Prep flow")
parser.add_argument("--input_sales", default="data/sales.csv")
parser.add_argument("--output_results", default="output/results.csv")
args = parser.parse_args()
result = main(input_sales=args.input_sales, output_results=args.output_results)
print(f"Processing complete. Output shape: {result.shape}")
Supported Transformations
Node Types
| Node | Description |
|---|---|
| Input | CSV, Excel, JSON, PostgreSQL, MySQL, SAP HANA |
| Clean / Transform | Rename, filter, calculate, type conversion, and more |
| Pivot | Columns-to-rows (melt) and rows-to-columns (pivot_table) |
| Aggregate | groupby with all common aggregations |
| Join | Inner, left, right, full outer |
| Union | Vertical concatenation |
| Filter | Boolean conditions |
| Output | CSV, Excel, databases |
Input Sources
| Tableau | Pandas |
|---|---|
| CSV File | pd.read_csv() |
| Excel File | pd.read_excel() |
| JSON File | pd.read_json() |
| PostgreSQL | pd.read_sql() with psycopg2 |
| MySQL | pd.read_sql() with mysql-connector |
| SAP HANA | pd.read_sql() with hdbcli |
Clean Operations
| Tableau | Pandas |
|---|---|
| Rename | df.rename(columns={}) |
| Remove Field | df.drop(columns=[]) |
| Change Type | df.astype() / pd.to_datetime() |
| Filter | df[condition] |
| Replace Values | df.replace() |
| Split Field | df[col].str.split() |
| Merge Fields | String concatenation |
| Fill Nulls | df.fillna() |
| Remove Nulls | df.dropna() |
| Calculated Field | New column assignment |
| Trim | df[col].str.strip() |
| Case Conversion | str.upper() / str.lower() |
Pivot Operations
| Tableau | Pandas |
|---|---|
| Columns to Rows | pd.melt() |
| Rows to Columns | df.pivot_table() |
Aggregations
| Tableau | Pandas |
|---|---|
| SUM | sum |
| AVG | mean |
| COUNT | count |
| COUNTD | nunique |
| MIN | min |
| MAX | max |
| MEDIAN | median |
| STDEV | std |
Joins
| Tableau | Pandas |
|---|---|
| Inner | pd.merge(how='inner') |
| Left | pd.merge(how='left') |
| Right | pd.merge(how='right') |
| Full Outer | pd.merge(how='outer') |
Tableau Expression Syntax
| Tableau | Pandas |
|---|---|
[Field Name] |
df['Field Name'] |
IF...THEN...ELSE...END |
np.where() |
CONTAINS([Field], 'x') |
df['Field'].str.contains('x') |
LEFT([Field], n) |
df['Field'].str[:n] |
UPPER([Field]) |
df['Field'].str.upper() |
ZN([Field]) |
df['Field'].fillna(0) |
IFNULL([Field], val) |
df['Field'].fillna(val) |
YEAR([Date]) |
df['Date'].dt.year |
Architecture
tfl_decompiler/
├── __init__.py # Public API exports
├── __main__.py # python -m tfl_decompiler entry point
├── main.py # CLI entry point (tfl-decompile command)
├── extractor.py # .tfl ZIP extraction and JSON parsing
├── flow_parser.py # Flow graph construction + topological sort
├── node_registry.py # Node type → handler mapping
├── code_generator.py # Python code generation
├── expression_parser.py # Tableau calc → pandas expression translator
├── db_connectors.py # Database connection helpers
├── utils.py # Utility functions
├── nodes/
│ ├── base.py # Abstract base node
│ ├── input_node.py # Data source handlers
│ ├── clean_node.py # Transform handlers
│ ├── pivot_node.py # Pivot handlers
│ ├── aggregate_node.py # Aggregation handlers
│ ├── join_node.py # Join handlers
│ ├── union_node.py # Union handlers
│ ├── filter_node.py # Filter handlers
│ └── output_node.py # Output handlers
└── builder/
├── flow_builder.py # High-level flow assembly
└── node_builders.py # Per-node code builders
Why Replace Tableau Prep?
| Tableau Prep | Python / pandas |
|---|---|
| Proprietary format | Plain text, version-controlled |
| GUI-only | Scriptable and automatable |
| License required | Free and open source |
| Limited scalability | Scale to any size |
| Black box | Full transparency |
| Desktop only | Runs anywhere |
Contributing
Contributions are welcome. Areas of interest:
- Additional node types
- More Tableau functions in the expression parser
- Additional database connectors
- Test cases with real
.tflfiles
Please open an issue or pull request on GitHub.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tfl_decompiler-1.0.2.tar.gz.
File metadata
- Download URL: tfl_decompiler-1.0.2.tar.gz
- Upload date:
- Size: 71.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56de4557427aeb9e27d740fda03e01fae2c8f3328625db53d563326c32244881
|
|
| MD5 |
e1266e85eaea03d4a11e560425fc030a
|
|
| BLAKE2b-256 |
5b09a5d4b2e37ec2c261435282185026211ab65750e6079393fa4306941404f1
|
File details
Details for the file tfl_decompiler-1.0.2-py3-none-any.whl.
File metadata
- Download URL: tfl_decompiler-1.0.2-py3-none-any.whl
- Upload date:
- Size: 83.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d8c182ab6fcc7435a85e3d72d90a24f8808ea412e9fdb5c59da83da7c2b4cf5
|
|
| MD5 |
196e99f7500562202001f9ae30239603
|
|
| BLAKE2b-256 |
fc197fe9c3f82fe56967a3efe7a94fb9e0a730ebd42c7c0f2f5b674c50940bcb
|