tableau-cli 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tableau_cli-0.1.1/.claude/settings.local.json +18 -0
- tableau_cli-0.1.1/.claude/skills/tableau-cli +1 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/PKG-INFO +44 -8
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/README.md +42 -6
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/pyproject.toml +2 -2
- tableau_cli-0.1.1/skills/SKILL.md +51 -0
- tableau_cli-0.1.1/skills/references/cli.md +282 -0
- tableau_cli-0.1.1/skills/references/installation.md +46 -0
- tableau_cli-0.1.1/skills-lock.json +10 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/cli.py +1 -1
- tableau_cli-0.1.1/src/tableau_cli/commands/convert_cmd.py +62 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/commands/datasources_cmd.py +27 -6
- tableau_cli-0.1.0/src/tableau_cli/commands/convert_cmd.py → tableau_cli-0.1.1/src/tableau_cli/utils/convert.py +23 -59
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/.gitignore +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/LICENSE +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/NOTICE +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/api/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/api/client.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/auth/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/auth/with_auth.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/commands/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/commands/config_cmd.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/commands/search_cmd.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/commands/views_cmd.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/commands/workbooks_cmd.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/config/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/config/store.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/config/types.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/errors/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/errors/cli_error.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/errors/vds_error_handler.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/output/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/output/format.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/output/json_output.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/output/table_output.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/utils/__init__.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/utils/datasource_metadata_utils.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/utils/paginate.py +0 -0
- {tableau_cli-0.1.0 → tableau_cli-0.1.1}/src/tableau_cli/utils/search_content_utils.py +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(python:*)",
|
|
5
|
+
"Bash(echo \"EXIT: $?\")",
|
|
6
|
+
"Bash(tableau-cli datasources:*)",
|
|
7
|
+
"Bash(tableau-cli convert:*)",
|
|
8
|
+
"Bash(ruff check:*)",
|
|
9
|
+
"Bash(git add:*)",
|
|
10
|
+
"Bash(git commit:*)",
|
|
11
|
+
"Bash(git push:*)",
|
|
12
|
+
"Bash(pip index:*)",
|
|
13
|
+
"Bash(pip install:*)",
|
|
14
|
+
"Bash(bash -l -c \"history\")",
|
|
15
|
+
"Bash(twine --version)"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../../.agents/skills/tableau-cli
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tableau-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: CLI for Tableau Server/Cloud, designed for AI agent integration
|
|
5
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Repository, https://github.com/i-richardwang/tableau-cli
|
|
6
6
|
Author: Richard Wang
|
|
7
7
|
License-Expression: Apache-2.0
|
|
8
8
|
License-File: LICENSE
|
|
@@ -34,7 +34,11 @@ A command-line interface for Tableau Server / Tableau Cloud, designed for AI age
|
|
|
34
34
|
|
|
35
35
|
## Why CLI over MCP?
|
|
36
36
|
|
|
37
|
-
MCP (Model Context Protocol) servers continuously occupy agent context. A CLI
|
|
37
|
+
MCP (Model Context Protocol) servers continuously occupy agent context — every tool description is loaded into the system prompt, even when only one command is needed. A CLI follows a simpler call-execute-exit pattern: the agent invokes a command, reads the JSON output, and moves on.
|
|
38
|
+
|
|
39
|
+
The companion Claude Code skill under `skills/` extends the same idea to command knowledge — the full reference is loaded only when the agent is about to execute a command, not kept in context upfront. See [Use with Claude Code](#use-with-claude-code) below.
|
|
40
|
+
|
|
41
|
+
This project provides the same capabilities as the [tableau-mcp](https://github.com/tableau/tableau-mcp) server, with behavioral alignment in data transformations, error handling, and output structures.
|
|
38
42
|
|
|
39
43
|
## Quick Start
|
|
40
44
|
|
|
@@ -47,7 +51,7 @@ MCP (Model Context Protocol) servers continuously occupy agent context. A CLI to
|
|
|
47
51
|
### Install
|
|
48
52
|
|
|
49
53
|
```bash
|
|
50
|
-
git clone
|
|
54
|
+
git clone https://github.com/i-richardwang/tableau-cli.git
|
|
51
55
|
cd tableau-cli
|
|
52
56
|
pip install -e .
|
|
53
57
|
|
|
@@ -79,6 +83,29 @@ Environment variables take precedence over the config file. `siteName` defaults
|
|
|
79
83
|
tableau-cli config show
|
|
80
84
|
```
|
|
81
85
|
|
|
86
|
+
## Use with Claude Code
|
|
87
|
+
|
|
88
|
+
This repository ships with a Claude Code skill under `skills/`. Once loaded, it gives an agent enough context to route a user's request to the right command — without putting the full CLI reference into the system prompt.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
skills/
|
|
92
|
+
├── SKILL.md # Intent routing + environment check
|
|
93
|
+
└── references/
|
|
94
|
+
├── cli.md # Full command reference (loaded before executing)
|
|
95
|
+
└── installation.md # Install + auth setup (loaded if not configured)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Once Claude Code has loaded the skill, a typical interaction looks like:
|
|
99
|
+
|
|
100
|
+
1. The agent runs `tableau-cli --help` to verify the CLI is installed and configured. If not, it loads `references/installation.md` and stops until setup is complete.
|
|
101
|
+
2. The agent maps the user's intent to a subcommand using the Intent Routing table in `SKILL.md` (e.g., "find datasources with Sales in the name" → `ds list --filter "name:has:Sales"`).
|
|
102
|
+
3. Before constructing the actual command, the agent loads `references/cli.md` — the skill explicitly forbids guessing flags from memory, so the reference is the source of truth for syntax.
|
|
103
|
+
4. The agent runs the command and parses the structured JSON output, including the `hint` field on errors.
|
|
104
|
+
|
|
105
|
+
Common workflows (e.g., `ds download --to parquet → load with Polars/Pandas`) are pre-defined under Intent Routing in `SKILL.md`, so the agent doesn't have to reason about chaining from scratch.
|
|
106
|
+
|
|
107
|
+
You can of course use `tableau-cli` directly from a shell without the skill — the skill is only needed when you want an agent to drive the tool.
|
|
108
|
+
|
|
82
109
|
## Commands
|
|
83
110
|
|
|
84
111
|
### Search
|
|
@@ -101,6 +128,10 @@ tableau-cli ds list --filter "name:has:Sales" --limit 50
|
|
|
101
128
|
# Download datasource file (.tdsx)
|
|
102
129
|
tableau-cli datasources download <datasourceId> -o ./data/
|
|
103
130
|
|
|
131
|
+
# Download and convert to Parquet or CSV in one step (requires tableau-cli[convert])
|
|
132
|
+
tableau-cli ds download <datasourceId> -o ./data/ --to parquet
|
|
133
|
+
tableau-cli ds download <datasourceId> -o ./data/ --to csv
|
|
134
|
+
|
|
104
135
|
# Get field metadata (VizQL Data Service + Metadata API enrichment)
|
|
105
136
|
tableau-cli datasources metadata <luid>
|
|
106
137
|
|
|
@@ -126,7 +157,9 @@ tableau-cli views image <viewId> --width 1200 --height 800 --img-format SVG -o d
|
|
|
126
157
|
|
|
127
158
|
### Convert
|
|
128
159
|
|
|
129
|
-
Convert
|
|
160
|
+
Convert local TDSX/HYPER files to Parquet or CSV. Requires `pip install tableau-cli[convert]`.
|
|
161
|
+
|
|
162
|
+
For most use cases, `ds download --to parquet` (or `--to csv`) is simpler — it downloads and converts in one step. The `convert` command is useful when you already have a `.tdsx` or `.hyper` file on disk.
|
|
130
163
|
|
|
131
164
|
```bash
|
|
132
165
|
# Convert TDSX to Parquet (default)
|
|
@@ -176,9 +209,12 @@ tableau-cli ds list --format table
|
|
|
176
209
|
Commands that save files (`ds download`, `views image -o`, `convert`) output a JSON object with the file path to stdout, enabling agents to chain operations:
|
|
177
210
|
|
|
178
211
|
```bash
|
|
179
|
-
# Download and convert
|
|
180
|
-
tableau-cli ds download <id> -o ./data/
|
|
181
|
-
|
|
212
|
+
# Download and convert in one step
|
|
213
|
+
tableau-cli ds download <id> -o ./data/ --to parquet # → {"filePath": ".../data.parquet"}
|
|
214
|
+
|
|
215
|
+
# Or as separate steps (useful for keeping the original .tdsx)
|
|
216
|
+
tableau-cli ds download <id> -o ./data/ # → {"filePath": ".../data.tdsx"}
|
|
217
|
+
tableau-cli convert ./data/data.tdsx -o ./data/ # → {"filePath": ".../data.parquet"}
|
|
182
218
|
```
|
|
183
219
|
|
|
184
220
|
### Error Output
|
|
@@ -4,7 +4,11 @@ A command-line interface for Tableau Server / Tableau Cloud, designed for AI age
|
|
|
4
4
|
|
|
5
5
|
## Why CLI over MCP?
|
|
6
6
|
|
|
7
|
-
MCP (Model Context Protocol) servers continuously occupy agent context. A CLI
|
|
7
|
+
MCP (Model Context Protocol) servers continuously occupy agent context — every tool description is loaded into the system prompt, even when only one command is needed. A CLI follows a simpler call-execute-exit pattern: the agent invokes a command, reads the JSON output, and moves on.
|
|
8
|
+
|
|
9
|
+
The companion Claude Code skill under `skills/` extends the same idea to command knowledge — the full reference is loaded only when the agent is about to execute a command, not kept in context upfront. See [Use with Claude Code](#use-with-claude-code) below.
|
|
10
|
+
|
|
11
|
+
This project provides the same capabilities as the [tableau-mcp](https://github.com/tableau/tableau-mcp) server, with behavioral alignment in data transformations, error handling, and output structures.
|
|
8
12
|
|
|
9
13
|
## Quick Start
|
|
10
14
|
|
|
@@ -17,7 +21,7 @@ MCP (Model Context Protocol) servers continuously occupy agent context. A CLI to
|
|
|
17
21
|
### Install
|
|
18
22
|
|
|
19
23
|
```bash
|
|
20
|
-
git clone
|
|
24
|
+
git clone https://github.com/i-richardwang/tableau-cli.git
|
|
21
25
|
cd tableau-cli
|
|
22
26
|
pip install -e .
|
|
23
27
|
|
|
@@ -49,6 +53,29 @@ Environment variables take precedence over the config file. `siteName` defaults
|
|
|
49
53
|
tableau-cli config show
|
|
50
54
|
```
|
|
51
55
|
|
|
56
|
+
## Use with Claude Code
|
|
57
|
+
|
|
58
|
+
This repository ships with a Claude Code skill under `skills/`. Once loaded, it gives an agent enough context to route a user's request to the right command — without putting the full CLI reference into the system prompt.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
skills/
|
|
62
|
+
├── SKILL.md # Intent routing + environment check
|
|
63
|
+
└── references/
|
|
64
|
+
├── cli.md # Full command reference (loaded before executing)
|
|
65
|
+
└── installation.md # Install + auth setup (loaded if not configured)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Once Claude Code has loaded the skill, a typical interaction looks like:
|
|
69
|
+
|
|
70
|
+
1. The agent runs `tableau-cli --help` to verify the CLI is installed and configured. If not, it loads `references/installation.md` and stops until setup is complete.
|
|
71
|
+
2. The agent maps the user's intent to a subcommand using the Intent Routing table in `SKILL.md` (e.g., "find datasources with Sales in the name" → `ds list --filter "name:has:Sales"`).
|
|
72
|
+
3. Before constructing the actual command, the agent loads `references/cli.md` — the skill explicitly forbids guessing flags from memory, so the reference is the source of truth for syntax.
|
|
73
|
+
4. The agent runs the command and parses the structured JSON output, including the `hint` field on errors.
|
|
74
|
+
|
|
75
|
+
Common workflows (e.g., `ds download --to parquet → load with Polars/Pandas`) are pre-defined under Intent Routing in `SKILL.md`, so the agent doesn't have to reason about chaining from scratch.
|
|
76
|
+
|
|
77
|
+
You can of course use `tableau-cli` directly from a shell without the skill — the skill is only needed when you want an agent to drive the tool.
|
|
78
|
+
|
|
52
79
|
## Commands
|
|
53
80
|
|
|
54
81
|
### Search
|
|
@@ -71,6 +98,10 @@ tableau-cli ds list --filter "name:has:Sales" --limit 50
|
|
|
71
98
|
# Download datasource file (.tdsx)
|
|
72
99
|
tableau-cli datasources download <datasourceId> -o ./data/
|
|
73
100
|
|
|
101
|
+
# Download and convert to Parquet or CSV in one step (requires tableau-cli[convert])
|
|
102
|
+
tableau-cli ds download <datasourceId> -o ./data/ --to parquet
|
|
103
|
+
tableau-cli ds download <datasourceId> -o ./data/ --to csv
|
|
104
|
+
|
|
74
105
|
# Get field metadata (VizQL Data Service + Metadata API enrichment)
|
|
75
106
|
tableau-cli datasources metadata <luid>
|
|
76
107
|
|
|
@@ -96,7 +127,9 @@ tableau-cli views image <viewId> --width 1200 --height 800 --img-format SVG -o d
|
|
|
96
127
|
|
|
97
128
|
### Convert
|
|
98
129
|
|
|
99
|
-
Convert
|
|
130
|
+
Convert local TDSX/HYPER files to Parquet or CSV. Requires `pip install tableau-cli[convert]`.
|
|
131
|
+
|
|
132
|
+
For most use cases, `ds download --to parquet` (or `--to csv`) is simpler — it downloads and converts in one step. The `convert` command is useful when you already have a `.tdsx` or `.hyper` file on disk.
|
|
100
133
|
|
|
101
134
|
```bash
|
|
102
135
|
# Convert TDSX to Parquet (default)
|
|
@@ -146,9 +179,12 @@ tableau-cli ds list --format table
|
|
|
146
179
|
Commands that save files (`ds download`, `views image -o`, `convert`) output a JSON object with the file path to stdout, enabling agents to chain operations:
|
|
147
180
|
|
|
148
181
|
```bash
|
|
149
|
-
# Download and convert
|
|
150
|
-
tableau-cli ds download <id> -o ./data/
|
|
151
|
-
|
|
182
|
+
# Download and convert in one step
|
|
183
|
+
tableau-cli ds download <id> -o ./data/ --to parquet # → {"filePath": ".../data.parquet"}
|
|
184
|
+
|
|
185
|
+
# Or as separate steps (useful for keeping the original .tdsx)
|
|
186
|
+
tableau-cli ds download <id> -o ./data/ # → {"filePath": ".../data.tdsx"}
|
|
187
|
+
tableau-cli convert ./data/data.tdsx -o ./data/ # → {"filePath": ".../data.parquet"}
|
|
152
188
|
```
|
|
153
189
|
|
|
154
190
|
### Error Output
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tableau-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.1"
|
|
8
8
|
description = "CLI for Tableau Server/Cloud, designed for AI agent integration"
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -30,7 +30,7 @@ dependencies = [
|
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.urls]
|
|
33
|
-
Repository = "https://github.com/
|
|
33
|
+
Repository = "https://github.com/i-richardwang/tableau-cli"
|
|
34
34
|
|
|
35
35
|
[project.optional-dependencies]
|
|
36
36
|
convert = [
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tableau-cli
|
|
3
|
+
description: "Interact with Tableau Server / Cloud via the tableau-cli command: search content, query datasources, download files, export view images and data, convert TDSX/HYPER to Parquet/CSV. Use when the user wants to find, download, query, or export any Tableau content."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Tableau CLI
|
|
7
|
+
|
|
8
|
+
Interact with Tableau Server / Cloud via the `tableau-cli` command.
|
|
9
|
+
|
|
10
|
+
## References
|
|
11
|
+
|
|
12
|
+
Load these files when needed — do not load all of them upfront:
|
|
13
|
+
|
|
14
|
+
- [references/cli.md](references/cli.md) — full CLI command reference. Load before running any `tableau-cli` command or looking up flags and syntax.
|
|
15
|
+
- [references/installation.md](references/installation.md) — installation and authentication setup. Load if `tableau-cli` is not installed or not configured.
|
|
16
|
+
|
|
17
|
+
## Environment Check
|
|
18
|
+
|
|
19
|
+
Before running any command, verify the CLI is available:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
tableau-cli --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
If `tableau-cli` is not installed or the API key is missing, load [references/installation.md](references/installation.md) and stop until setup is complete.
|
|
26
|
+
|
|
27
|
+
## CLI Reference
|
|
28
|
+
|
|
29
|
+
Before running any `tableau-cli` command, load [references/cli.md](references/cli.md) as the source of truth for exact flags, subcommands, and examples. Do not guess command syntax from memory.
|
|
30
|
+
|
|
31
|
+
## Intent Routing
|
|
32
|
+
|
|
33
|
+
The following requests can be handled directly with CLI commands. Load [references/cli.md](references/cli.md) and execute:
|
|
34
|
+
|
|
35
|
+
- Search content across types — `search`
|
|
36
|
+
- List/filter datasources, views, or workbooks — `ds list` / `views list` / `wb list`
|
|
37
|
+
- Download a datasource file — `ds download` (supports `--to parquet` / `--to csv` for direct conversion)
|
|
38
|
+
- Inspect datasource field metadata — `ds metadata`
|
|
39
|
+
- Query data from a datasource — `ds query`
|
|
40
|
+
- Export view data as CSV — `views data`
|
|
41
|
+
- Export view as image — `views image`
|
|
42
|
+
- View workbook details and its views — `wb get`
|
|
43
|
+
- Convert TDSX/HYPER to Parquet/CSV — `convert`
|
|
44
|
+
|
|
45
|
+
Common combined workflows:
|
|
46
|
+
|
|
47
|
+
**Download datasource for local analysis**: `ds list` → `ds download --to parquet` → load with Polars/Pandas
|
|
48
|
+
|
|
49
|
+
**Quick data extraction (no file download)**: `ds metadata` → `ds query` (requires VizQL Data Service; fall back to download + convert if unavailable)
|
|
50
|
+
|
|
51
|
+
**Export dashboard screenshot**: `views list` → `views image`
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tableau-cli-reference
|
|
3
|
+
description: Full CLI command reference for tableau-cli. Load before running any command as the authoritative source for flags and syntax.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CLI Command Reference
|
|
7
|
+
|
|
8
|
+
All commands output JSON (indent=2) by default. List commands support `--format table` for human-readable output.
|
|
9
|
+
|
|
10
|
+
Errors are also structured JSON:
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"isError": true,
|
|
14
|
+
"errorType": "<type>",
|
|
15
|
+
"message": "<description>",
|
|
16
|
+
"hint": "<suggested action>"
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## search
|
|
23
|
+
|
|
24
|
+
Search across all content types on the site.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
tableau-cli search "keyword"
|
|
28
|
+
tableau-cli search "keyword" --type workbook,view,datasource
|
|
29
|
+
tableau-cli search "keyword" --limit 10 --order-by hitsTotal:desc
|
|
30
|
+
tableau-cli search --format table
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Option | Type | Default | Description |
|
|
34
|
+
|--------|------|---------|-------------|
|
|
35
|
+
| `terms` | string (positional) | none | Search terms, optional |
|
|
36
|
+
| `--type` | string | none | Comma-separated content types (workbook, view, datasource, etc.) |
|
|
37
|
+
| `--limit` | int | 100 | Max results to return |
|
|
38
|
+
| `--order-by` | string | none | Sort method (e.g., `hitsTotal:desc`) |
|
|
39
|
+
| `--format` | json / table | json | Output format |
|
|
40
|
+
|
|
41
|
+
View results include `parentWorkbookName` (parent workbook) and `totalViewCount` (total views).
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## datasources (alias: ds)
|
|
46
|
+
|
|
47
|
+
### ds list
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
tableau-cli ds list
|
|
51
|
+
tableau-cli ds list --filter "name:has:keyword" --limit 50 --format table
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| Option | Type | Default | Description |
|
|
55
|
+
|--------|------|---------|-------------|
|
|
56
|
+
| `--filter` | string | none | Server-side filter (see filter syntax below) |
|
|
57
|
+
| `--page-size` | int | none | Items per page |
|
|
58
|
+
| `--limit` | int | none | Max total results |
|
|
59
|
+
| `--format` | json / table | json | Output format |
|
|
60
|
+
|
|
61
|
+
Automatically paginates until all results are fetched or limit is reached.
|
|
62
|
+
|
|
63
|
+
### ds download
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
tableau-cli ds download <datasource-id> -o ./output/
|
|
67
|
+
tableau-cli ds download <datasource-id> -o ./output/ --to parquet
|
|
68
|
+
tableau-cli ds download <datasource-id> -o ./output/ --to csv
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Option | Type | Default | Description |
|
|
72
|
+
|--------|------|---------|-------------|
|
|
73
|
+
| `datasource_id` | string (positional) | required | Datasource LUID |
|
|
74
|
+
| `-o, --output` | path | `.` | Output path (directory or filename) |
|
|
75
|
+
| `--to` | tdsx / parquet / csv | tdsx | Output format; parquet/csv automatically converts after download |
|
|
76
|
+
|
|
77
|
+
Output: `{"filePath": "<absolute path>"}`
|
|
78
|
+
|
|
79
|
+
When `--to parquet` or `--to csv` is specified, the datasource is downloaded, converted in a temporary directory, and only the final file is written to the output path. Requires `tableau-cli[convert]` extras.
|
|
80
|
+
|
|
81
|
+
### ds metadata
|
|
82
|
+
|
|
83
|
+
View datasource field metadata. Requires VizQL Data Service.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
tableau-cli ds metadata <luid>
|
|
87
|
+
tableau-cli ds metadata <luid> --format table
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| Option | Type | Default | Description |
|
|
91
|
+
|--------|------|---------|-------------|
|
|
92
|
+
| `luid` | string (positional) | required | Datasource LUID |
|
|
93
|
+
| `--format` | json / table | json | Output format |
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
- `datasourceDescription` — datasource description
|
|
97
|
+
- `fieldGroups[].fields[]` — field list (name, dataType, role, formula, description, etc.)
|
|
98
|
+
- `parameters[]` — parameter list (name, parameterType, dataType, value, etc.)
|
|
99
|
+
|
|
100
|
+
Attempts to enrich via GraphQL Metadata API; falls back to base VizQL metadata if unavailable.
|
|
101
|
+
|
|
102
|
+
### ds query
|
|
103
|
+
|
|
104
|
+
Query datasource data directly. Requires VizQL Data Service.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
tableau-cli ds query <luid> --query '{"fields": [{"fieldCaption": "Category"}, {"fieldCaption": "Sales"}]}'
|
|
108
|
+
tableau-cli ds query <luid> --query '{"fields": [...]}' --limit 100 --format table
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| Option | Type | Default | Description |
|
|
112
|
+
|--------|------|---------|-------------|
|
|
113
|
+
| `luid` | string (positional) | required | Datasource LUID |
|
|
114
|
+
| `--query` | JSON string | required | Query definition (fields, filters, etc.) |
|
|
115
|
+
| `--limit` | int | none | Row limit (client-side truncation) |
|
|
116
|
+
| `--format` | json / table | json | Output format |
|
|
117
|
+
|
|
118
|
+
`--query` must be valid JSON; otherwise reports `invalid-input` error. `--limit` truncates client-side, not server-side.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## views
|
|
123
|
+
|
|
124
|
+
Views include worksheets (Sheets), dashboards, and stories.
|
|
125
|
+
|
|
126
|
+
### views list
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
tableau-cli views list
|
|
130
|
+
tableau-cli views list --filter "name:has:keyword" --format table
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
| Option | Type | Default | Description |
|
|
134
|
+
|--------|------|---------|-------------|
|
|
135
|
+
| `--filter` | string | none | Server-side filter |
|
|
136
|
+
| `--page-size` | int | none | Items per page |
|
|
137
|
+
| `--limit` | int | none | Max total results |
|
|
138
|
+
| `--format` | json / table | json | Output format |
|
|
139
|
+
|
|
140
|
+
Results include `workbookId` to trace back to the parent workbook.
|
|
141
|
+
|
|
142
|
+
### views data
|
|
143
|
+
|
|
144
|
+
Export view data as CSV.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
tableau-cli views data <view-id>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
| Option | Type | Description |
|
|
151
|
+
|--------|------|-------------|
|
|
152
|
+
| `view_id` | string (positional) | View LUID |
|
|
153
|
+
|
|
154
|
+
Output: CSV text written directly to stdout (not JSON).
|
|
155
|
+
|
|
156
|
+
### views image
|
|
157
|
+
|
|
158
|
+
Export view as image.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
tableau-cli views image <view-id> -o output.png
|
|
162
|
+
tableau-cli views image <view-id> --width 1200 --height 800 -o output.png
|
|
163
|
+
tableau-cli views image <view-id> --img-format SVG -o output.svg
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
| Option | Type | Default | Description |
|
|
167
|
+
|--------|------|---------|-------------|
|
|
168
|
+
| `view_id` | string (positional) | required | View LUID |
|
|
169
|
+
| `--width` | int | none | Image width in pixels |
|
|
170
|
+
| `--height` | int | none | Image height in pixels |
|
|
171
|
+
| `--img-format` | PNG / SVG | PNG | Image format |
|
|
172
|
+
| `-o, --output` | path | none | Output file path |
|
|
173
|
+
|
|
174
|
+
With `-o`: outputs `{"filePath": "<absolute path>"}`. Without `-o`: base64-encoded image to stdout.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## workbooks (alias: wb)
|
|
179
|
+
|
|
180
|
+
### wb list
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
tableau-cli wb list
|
|
184
|
+
tableau-cli wb list --filter "name:has:keyword" --format table
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
| Option | Type | Default | Description |
|
|
188
|
+
|--------|------|---------|-------------|
|
|
189
|
+
| `--filter` | string | none | Server-side filter |
|
|
190
|
+
| `--page-size` | int | none | Items per page |
|
|
191
|
+
| `--limit` | int | none | Max total results |
|
|
192
|
+
| `--format` | json / table | json | Output format |
|
|
193
|
+
|
|
194
|
+
### wb get
|
|
195
|
+
|
|
196
|
+
View workbook details, including all views with usage statistics.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
tableau-cli wb get <workbook-id>
|
|
200
|
+
tableau-cli wb get <workbook-id> --format table
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
| Option | Type | Default | Description |
|
|
204
|
+
|--------|------|---------|-------------|
|
|
205
|
+
| `workbook_id` | string (positional) | required | Workbook LUID |
|
|
206
|
+
| `--format` | json / table | json | Output format |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## convert
|
|
211
|
+
|
|
212
|
+
Local file conversion: TDSX/HYPER → Parquet/CSV. Requires `tableau-cli[convert]`.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
tableau-cli convert data.tdsx
|
|
216
|
+
tableau-cli convert data.tdsx -o ./output/
|
|
217
|
+
tableau-cli convert data.tdsx --to csv -o ./output/
|
|
218
|
+
tableau-cli convert extract.hyper -o ./output/result.parquet
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
| Option | Type | Default | Description |
|
|
222
|
+
|--------|------|---------|-------------|
|
|
223
|
+
| `input_path` | path (positional) | required | Input file (.tdsx or .hyper) |
|
|
224
|
+
| `--to` | parquet / csv | parquet | Output format |
|
|
225
|
+
| `-o, --output` | path | none | Output path (directory or filename); defaults to input file directory |
|
|
226
|
+
|
|
227
|
+
Output: `{"filePath": "<absolute path>"}`
|
|
228
|
+
|
|
229
|
+
Reports `missing-dependencies` error if convert extras are not installed, with hint to run `pip install "tableau-cli[convert]"`.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## config
|
|
234
|
+
|
|
235
|
+
### config show
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
tableau-cli config show
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Outputs current configuration as JSON. PAT value is masked as `"****"`.
|
|
242
|
+
|
|
243
|
+
### config set
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
tableau-cli config set --server https://... --site-name ... --pat-name ... --pat-value ...
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
All options are optional; only updates the specified fields. Saves to `~/.tableau-cli.json`.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Filter Syntax
|
|
254
|
+
|
|
255
|
+
The `--filter` option uses `field:operator:value` format:
|
|
256
|
+
|
|
257
|
+
| Operator | Meaning | Example |
|
|
258
|
+
|----------|---------|---------|
|
|
259
|
+
| `eq` | Exact match | `name:eq:Superstore` |
|
|
260
|
+
| `has` | Contains | `name:has:Sales` |
|
|
261
|
+
| `in` | Multi-value match | `name:in:[A,B,C]` |
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Intent → Command Quick Reference
|
|
266
|
+
|
|
267
|
+
| User wants to… | Command |
|
|
268
|
+
|-----------------|---------|
|
|
269
|
+
| Search content (any type) | `search "keyword"` |
|
|
270
|
+
| Find datasources | `ds list --filter "name:has:..."` |
|
|
271
|
+
| Download a datasource file | `ds download <id> -o dir/` |
|
|
272
|
+
| Download datasource as Parquet | `ds download <id> -o dir/ --to parquet` |
|
|
273
|
+
| Download datasource as CSV | `ds download <id> -o dir/ --to csv` |
|
|
274
|
+
| Inspect datasource fields | `ds metadata <luid>` |
|
|
275
|
+
| Query data from a datasource | `ds query <luid> --query '{...}'` |
|
|
276
|
+
| Find a dashboard/view | `views list --filter "name:has:..."` |
|
|
277
|
+
| Export view data | `views data <view-id>` |
|
|
278
|
+
| Export view screenshot | `views image <view-id> -o file.png` |
|
|
279
|
+
| Find workbooks | `wb list --filter "name:has:..."` |
|
|
280
|
+
| See views in a workbook | `wb get <workbook-id>` |
|
|
281
|
+
| Convert local TDSX to Parquet | `convert file.tdsx -o dir/` |
|
|
282
|
+
| Download and convert for analysis | `ds download <id> --to parquet` |
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tableau-cli-installation
|
|
3
|
+
description: Installation and authentication setup for tableau-cli. Load when the CLI is not installed or not configured.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install "tableau-cli[convert]"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Without `[convert]`, only core features are available (search, datasources, views, workbooks) — the `convert` command will not work.
|
|
13
|
+
|
|
14
|
+
## Authentication
|
|
15
|
+
|
|
16
|
+
tableau-cli uses Personal Access Token (PAT) authentication.
|
|
17
|
+
|
|
18
|
+
### Option 1: CLI config (saved to ~/.tableau-cli.json)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
tableau-cli config set \
|
|
22
|
+
--server https://your-tableau-server.com \
|
|
23
|
+
--site-name YourSite \
|
|
24
|
+
--pat-name your-token-name \
|
|
25
|
+
--pat-value your-token-value
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Option 2: Environment variables
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
export SERVER=https://your-tableau-server.com
|
|
32
|
+
export SITE_NAME=YourSite
|
|
33
|
+
export PAT_NAME=your-token-name
|
|
34
|
+
export PAT_VALUE=your-token-value
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Environment variables take precedence over the config file. `SITE_NAME` defaults to `""` (the default site) if not set.
|
|
38
|
+
|
|
39
|
+
## Verification
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
tableau-cli config show
|
|
43
|
+
tableau-cli ds list --limit 1
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`config show` displays the current configuration (PAT value is masked). If `ds list` returns data, authentication is working.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from tempfile import TemporaryDirectory
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ..errors.cli_error import CliError
|
|
11
|
+
from ..output.format import output
|
|
12
|
+
from ..utils.convert import (
|
|
13
|
+
SUPPORTED_FORMATS,
|
|
14
|
+
check_convert_deps,
|
|
15
|
+
extract_hyper_from_tdsx,
|
|
16
|
+
read_hyper,
|
|
17
|
+
write_df,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.command("convert")
|
|
22
|
+
@click.argument("input_path", type=click.Path(exists=True))
|
|
23
|
+
@click.option(
|
|
24
|
+
"--to", "to_fmt", default="parquet", type=click.Choice(SUPPORTED_FORMATS), help="Output format (default: parquet)"
|
|
25
|
+
)
|
|
26
|
+
@click.option("-o", "--output", "output_path", default=None, help="Output file or directory (default: same as input)")
|
|
27
|
+
def convert_command(input_path, to_fmt, output_path):
|
|
28
|
+
"""Convert TDSX/HYPER files to Parquet or CSV format."""
|
|
29
|
+
check_convert_deps()
|
|
30
|
+
|
|
31
|
+
input_p = Path(input_path)
|
|
32
|
+
suffix = input_p.suffix.lower()
|
|
33
|
+
|
|
34
|
+
if suffix not in (".tdsx", ".hyper"):
|
|
35
|
+
raise CliError(
|
|
36
|
+
error_type="invalid-input",
|
|
37
|
+
message=f"Unsupported file type: {suffix}",
|
|
38
|
+
hint="Supported formats: .tdsx, .hyper",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Resolve output path
|
|
42
|
+
ext = f".{to_fmt}"
|
|
43
|
+
if output_path is None:
|
|
44
|
+
out_p = input_p.with_suffix(ext)
|
|
45
|
+
elif os.path.isdir(output_path):
|
|
46
|
+
out_p = Path(output_path) / f"{input_p.stem}{ext}"
|
|
47
|
+
else:
|
|
48
|
+
out_p = Path(output_path)
|
|
49
|
+
|
|
50
|
+
# Read hyper data
|
|
51
|
+
if suffix == ".hyper":
|
|
52
|
+
df = read_hyper(input_p)
|
|
53
|
+
else:
|
|
54
|
+
with TemporaryDirectory() as td:
|
|
55
|
+
hyper_path = extract_hyper_from_tdsx(input_p, Path(td))
|
|
56
|
+
df = read_hyper(hyper_path)
|
|
57
|
+
|
|
58
|
+
write_df(df, out_p, to_fmt)
|
|
59
|
+
|
|
60
|
+
abs_path = os.path.abspath(out_p)
|
|
61
|
+
sys.stderr.write(f"Converted to {abs_path}\n")
|
|
62
|
+
output({"filePath": abs_path}, "json")
|
|
@@ -51,19 +51,40 @@ def datasources_list(filter_, page_size, limit, fmt):
|
|
|
51
51
|
@click.option(
|
|
52
52
|
"-o", "--output", "output_path", default=".", help="Output file path or directory (default: current directory)"
|
|
53
53
|
)
|
|
54
|
-
|
|
55
|
-
""
|
|
54
|
+
@click.option(
|
|
55
|
+
"--to",
|
|
56
|
+
"to_fmt",
|
|
57
|
+
default="tdsx",
|
|
58
|
+
type=click.Choice(["tdsx", "parquet", "csv"]),
|
|
59
|
+
help="Output format (default: tdsx)",
|
|
60
|
+
)
|
|
61
|
+
def datasources_download(datasource_id, output_path, to_fmt):
|
|
62
|
+
"""Download a datasource file, optionally converting to Parquet or CSV."""
|
|
63
|
+
from pathlib import Path
|
|
64
|
+
|
|
56
65
|
config = resolve_config()
|
|
57
66
|
|
|
67
|
+
if to_fmt != "tdsx":
|
|
68
|
+
from ..utils.convert import check_convert_deps
|
|
69
|
+
|
|
70
|
+
check_convert_deps()
|
|
71
|
+
|
|
58
72
|
data, filename = with_auth(
|
|
59
73
|
config,
|
|
60
74
|
lambda api: api.download_datasource(datasource_id=datasource_id, site_id=api.site_id),
|
|
61
75
|
)
|
|
62
76
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
if to_fmt == "tdsx":
|
|
78
|
+
file_path = os.path.join(output_path, filename) if os.path.isdir(output_path) else output_path
|
|
79
|
+
with open(file_path, "wb") as f:
|
|
80
|
+
f.write(data)
|
|
81
|
+
else:
|
|
82
|
+
from ..utils.convert import convert_tdsx_bytes
|
|
83
|
+
|
|
84
|
+
stem = Path(filename).stem
|
|
85
|
+
out_name = f"{stem}.{to_fmt}"
|
|
86
|
+
file_path = os.path.join(output_path, out_name) if os.path.isdir(output_path) else output_path
|
|
87
|
+
convert_tdsx_bytes(data, Path(file_path), to_fmt)
|
|
67
88
|
|
|
68
89
|
abs_path = os.path.abspath(file_path)
|
|
69
90
|
sys.stderr.write(f"Downloaded to {abs_path}\n")
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
+
"""Shared helpers for converting TDSX / HYPER files to Parquet or CSV."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
4
|
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from tempfile import TemporaryDirectory
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
from zipfile import ZipFile
|
|
9
9
|
|
|
10
|
-
import click
|
|
11
|
-
|
|
12
10
|
from ..errors.cli_error import CliError
|
|
13
|
-
from ..output.format import output
|
|
14
11
|
|
|
15
12
|
if TYPE_CHECKING:
|
|
16
13
|
import polars as pl
|
|
@@ -19,7 +16,15 @@ CONVERT_DEPS = ("pantab", "polars", "pyarrow")
|
|
|
19
16
|
SUPPORTED_FORMATS = ("parquet", "csv")
|
|
20
17
|
|
|
21
18
|
|
|
22
|
-
def
|
|
19
|
+
def _is_importable(name: str) -> bool:
|
|
20
|
+
try:
|
|
21
|
+
__import__(name)
|
|
22
|
+
return True
|
|
23
|
+
except ImportError:
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def check_convert_deps() -> None:
|
|
23
28
|
"""Check that optional convert dependencies are installed."""
|
|
24
29
|
missing = [pkg for pkg in CONVERT_DEPS if not _is_importable(pkg)]
|
|
25
30
|
if missing:
|
|
@@ -30,15 +35,7 @@ def _check_convert_deps() -> None:
|
|
|
30
35
|
)
|
|
31
36
|
|
|
32
37
|
|
|
33
|
-
def
|
|
34
|
-
try:
|
|
35
|
-
__import__(name)
|
|
36
|
-
return True
|
|
37
|
-
except ImportError:
|
|
38
|
-
return False
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _extract_hyper_from_tdsx(tdsx_path: Path, target_dir: Path) -> Path:
|
|
38
|
+
def extract_hyper_from_tdsx(tdsx_path: Path, target_dir: Path) -> Path:
|
|
42
39
|
"""Extract the .hyper file from a TDSX archive."""
|
|
43
40
|
with ZipFile(tdsx_path) as zf:
|
|
44
41
|
members = [m for m in zf.namelist() if m.endswith(".hyper")]
|
|
@@ -63,7 +60,7 @@ def _extract_hyper_from_tdsx(tdsx_path: Path, target_dir: Path) -> Path:
|
|
|
63
60
|
return extracted
|
|
64
61
|
|
|
65
62
|
|
|
66
|
-
def
|
|
63
|
+
def read_hyper(hyper_path: Path) -> pl.DataFrame:
|
|
67
64
|
"""Read .hyper file and return as Polars DataFrame."""
|
|
68
65
|
import pantab
|
|
69
66
|
import polars as pl
|
|
@@ -83,7 +80,7 @@ def _read_hyper(hyper_path: Path) -> pl.DataFrame:
|
|
|
83
80
|
return pl.from_pandas(next(iter(frames.values())))
|
|
84
81
|
|
|
85
82
|
|
|
86
|
-
def
|
|
83
|
+
def write_df(df: pl.DataFrame, output_path: Path, to: str) -> None:
|
|
87
84
|
"""Write DataFrame to the specified format."""
|
|
88
85
|
if to == "parquet":
|
|
89
86
|
df.write_parquet(output_path)
|
|
@@ -91,45 +88,12 @@ def _write_df(df: pl.DataFrame, output_path: Path, to: str) -> None:
|
|
|
91
88
|
df.write_csv(output_path)
|
|
92
89
|
|
|
93
90
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
input_p = Path(input_path)
|
|
105
|
-
suffix = input_p.suffix.lower()
|
|
106
|
-
|
|
107
|
-
if suffix not in (".tdsx", ".hyper"):
|
|
108
|
-
raise CliError(
|
|
109
|
-
error_type="invalid-input",
|
|
110
|
-
message=f"Unsupported file type: {suffix}",
|
|
111
|
-
hint="Supported formats: .tdsx, .hyper",
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
# Resolve output path
|
|
115
|
-
ext = f".{to_fmt}"
|
|
116
|
-
if output_path is None:
|
|
117
|
-
out_p = input_p.with_suffix(ext)
|
|
118
|
-
elif os.path.isdir(output_path):
|
|
119
|
-
out_p = Path(output_path) / f"{input_p.stem}{ext}"
|
|
120
|
-
else:
|
|
121
|
-
out_p = Path(output_path)
|
|
122
|
-
|
|
123
|
-
# Read hyper data
|
|
124
|
-
if suffix == ".hyper":
|
|
125
|
-
df = _read_hyper(input_p)
|
|
126
|
-
else:
|
|
127
|
-
with TemporaryDirectory() as td:
|
|
128
|
-
hyper_path = _extract_hyper_from_tdsx(input_p, Path(td))
|
|
129
|
-
df = _read_hyper(hyper_path)
|
|
130
|
-
|
|
131
|
-
_write_df(df, out_p, to_fmt)
|
|
132
|
-
|
|
133
|
-
abs_path = os.path.abspath(out_p)
|
|
134
|
-
sys.stderr.write(f"Converted to {abs_path}\n")
|
|
135
|
-
output({"filePath": abs_path}, "json")
|
|
91
|
+
def convert_tdsx_bytes(data: bytes, output_path: Path, to_fmt: str) -> None:
|
|
92
|
+
"""Convert raw TDSX bytes directly to parquet/csv without persisting the intermediate file."""
|
|
93
|
+
with TemporaryDirectory() as td:
|
|
94
|
+
tmp_dir = Path(td)
|
|
95
|
+
tdsx_path = tmp_dir / "datasource.tdsx"
|
|
96
|
+
tdsx_path.write_bytes(data)
|
|
97
|
+
hyper_path = extract_hyper_from_tdsx(tdsx_path, tmp_dir)
|
|
98
|
+
df = read_hyper(hyper_path)
|
|
99
|
+
write_df(df, output_path, to_fmt)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|