tabpull 0.1.0__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.
- tabpull-0.1.0/LICENSE +21 -0
- tabpull-0.1.0/PKG-INFO +170 -0
- tabpull-0.1.0/README.md +139 -0
- tabpull-0.1.0/pyproject.toml +114 -0
- tabpull-0.1.0/setup.cfg +4 -0
- tabpull-0.1.0/src/tabpull/__init__.py +0 -0
- tabpull-0.1.0/src/tabpull/__main__.py +3 -0
- tabpull-0.1.0/src/tabpull/add.py +384 -0
- tabpull-0.1.0/src/tabpull/app.py +385 -0
- tabpull-0.1.0/src/tabpull/cli.py +17 -0
- tabpull-0.1.0/src/tabpull/embed.py +248 -0
- tabpull-0.1.0/src/tabpull/filters.py +172 -0
- tabpull-0.1.0/src/tabpull/home.py +187 -0
- tabpull-0.1.0/src/tabpull/jobs.py +201 -0
- tabpull-0.1.0/src/tabpull/remove.py +88 -0
- tabpull-0.1.0/src/tabpull/run.py +748 -0
- tabpull-0.1.0/src/tabpull/tableau.py +457 -0
- tabpull-0.1.0/src/tabpull/tui/__init__.py +3 -0
- tabpull-0.1.0/src/tabpull/tui/app.py +1078 -0
- tabpull-0.1.0/src/tabpull/tui/forms.py +757 -0
- tabpull-0.1.0/src/tabpull/ui.py +76 -0
- tabpull-0.1.0/src/tabpull/wizard.py +288 -0
- tabpull-0.1.0/src/tabpull.egg-info/PKG-INFO +170 -0
- tabpull-0.1.0/src/tabpull.egg-info/SOURCES.txt +31 -0
- tabpull-0.1.0/src/tabpull.egg-info/dependency_links.txt +1 -0
- tabpull-0.1.0/src/tabpull.egg-info/entry_points.txt +2 -0
- tabpull-0.1.0/src/tabpull.egg-info/requires.txt +5 -0
- tabpull-0.1.0/src/tabpull.egg-info/top_level.txt +1 -0
- tabpull-0.1.0/tests/test_cli.py +2512 -0
- tabpull-0.1.0/tests/test_crosstab.py +779 -0
- tabpull-0.1.0/tests/test_jobs.py +189 -0
- tabpull-0.1.0/tests/test_tui.py +1843 -0
- tabpull-0.1.0/tests/test_version.py +25 -0
tabpull-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Jeremy Castro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tabpull-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tabpull
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Export Tableau dashboard sheet crosstabs with filters
|
|
5
|
+
Author-email: Jeremy Castro <160854558+jer-castro@users.noreply.github.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jer-castro/tabpull
|
|
8
|
+
Project-URL: Repository, https://github.com/jer-castro/tabpull
|
|
9
|
+
Project-URL: Issues, https://github.com/jer-castro/tabpull/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/jer-castro/tabpull/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: tableau,crosstab,csv,cli
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: playwright==1.63.0
|
|
26
|
+
Requires-Dist: questionary==2.1.1
|
|
27
|
+
Requires-Dist: rich==15.0.0
|
|
28
|
+
Requires-Dist: tableauserverclient==0.41
|
|
29
|
+
Requires-Dist: textual==8.2.8
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="https://raw.githubusercontent.com/jer-castro/tabpull/main/assets/hero.svg" alt="tabpull" width="100%" />
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
tabpull exports named worksheets from a published Tableau dashboard to UTF-8 CSV.
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
pip install tabpull
|
|
40
|
+
uv tool install tabpull
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
That installs the `tabpull` command. Bare `tabpull` at a terminal opens an interactive screen. Add, setup, login, and run stay on that screen. `setup`, `add`, `remove`, `run`, and `login` are also subcommands and work the same with or without it. Pin a release with `tabpull==X.Y.Z` or `uv tool install tabpull==X.Y.Z`. Upgrade a uv tool with `uv tool upgrade tabpull`. A commit that is not published yet still installs with `uv tool install git+https://github.com/jer-castro/tabpull`. Add `@vX.Y.Z` on that URL to pin a tag.
|
|
44
|
+
|
|
45
|
+
tabpull launches installed Chrome, then Edge. When neither is installed, install Chromium after the package. After `pip install tabpull`, run `playwright install chromium` in that environment. After `uv tool install tabpull`, the `playwright` command is not on `PATH`. Run `uvx playwright==<version> install chromium` with the version tabpull prints when Chrome and Edge are missing. That command uses the same browser revision and the default Playwright cache as the installed package.
|
|
46
|
+
|
|
47
|
+
## Why Embedding API
|
|
48
|
+
|
|
49
|
+
REST crosstab and data endpoints only export the first sheet when the view is a dashboard. A worksheet that lives only inside a published dashboard is not published as its own view, so it has no REST view.
|
|
50
|
+
|
|
51
|
+
Those sheets go through the Embedding API (`exportCrosstabAsync`) in a headless browser signed in with your SSO session. The personal access token is used to find views.
|
|
52
|
+
|
|
53
|
+
## Setup
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
tabpull setup
|
|
57
|
+
tabpull setup --site finance # local name jobs will use
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The wizard asks for that name (unless you passed `--site`), a dashboard URL, and a personal access token. It checks the token, then opens a browser for SSO.
|
|
61
|
+
|
|
62
|
+
Run it again for another server or site. Each site keeps its own token and browser session. Re-running a name keeps the current values when you press Enter.
|
|
63
|
+
|
|
64
|
+
The browser is installed Chrome, then Edge. If neither is installed, tabpull prints `uvx playwright==<version> install chromium` for the Playwright version it has. It does not download a browser while Chrome or Edge is already there.
|
|
65
|
+
|
|
66
|
+
## Use
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
tabpull # interactive screen at a terminal; a listing when piped
|
|
70
|
+
tabpull add # search a view or paste its URL, pick sheets and filters
|
|
71
|
+
tabpull add --site finance \
|
|
72
|
+
--view SalesWorkbook/Overview \
|
|
73
|
+
--sheet "Order Detail" --sheet Totals \
|
|
74
|
+
--filter "Region=West|Central" \
|
|
75
|
+
--filter "Order Date=2026-09-01..2026-09-25 @Totals" \
|
|
76
|
+
--param "Top N=25" \
|
|
77
|
+
--name daily-west
|
|
78
|
+
tabpull run # every job, into the current folder
|
|
79
|
+
tabpull run daily-west
|
|
80
|
+
tabpull remove daily-west # delete that saved job
|
|
81
|
+
tabpull remove # pick jobs to delete
|
|
82
|
+
tabpull login # refresh SSO for the only site
|
|
83
|
+
tabpull login --site finance
|
|
84
|
+
tabpull --version
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
With no command at a terminal, tabpull opens a full-screen list of sites and jobs. Open a job to change its filters (values or range bounds), parameters, sheets, view, site, and name. Adding a filter or parameter, changing which field a row uses, and choosing sheets pick from the live view when it opens, and from the typed forms when it cannot, with the reason shown. Editing a value that is already chosen stays a form and does not open the view. Each change is checked and saved to the jobs file right away. `r` runs the selected or marked jobs, `a` adds, `d` removes after a confirm, `s` and `l` run setup and login. Run, add, setup, and login happen on the screen; escape or ctrl+c cancels a running step. The footer lists the keys, `?` shows all of them, `q` quits.
|
|
88
|
+
|
|
89
|
+
With no command and no terminal (a pipe or a script), tabpull prints configured sites, saved jobs, the jobs file, the output folder, and the next commands in [TOON](https://toonformat.dev/). It never waits for keys. Errors, including an unknown flag, print `error: ...` on stdout. A usage error exits 2. Any other failure exits 1.
|
|
90
|
+
|
|
91
|
+
`add` saves the job and prints the `run` command. It does not export. With `--view` and at least one `--sheet`, it writes the job and does not prompt. Leave those flags off and it asks.
|
|
92
|
+
|
|
93
|
+
`remove` deletes the named jobs and rewrites the jobs file. With no names, a terminal asks which jobs to delete and confirms before writing. With no terminal, pass the names. An unknown name exits 1 and lists the saved jobs, and the file is left unchanged. Removing every job leaves the file empty.
|
|
94
|
+
|
|
95
|
+
`add` with `--view` and `--sheet` still opens the view far enough to refuse a story: `Stories are not supported; use the dashboard inside it.` `run` refuses a story the same way, before it exports.
|
|
96
|
+
|
|
97
|
+
`--filter` is `Field=a|b` or `Field=min..max`. ` @Sheet` names the worksheet. Either side of a range may be omitted, but not both: `Field=min..` runs from that date through the latest value the filter allows, and `Field=..max` runs from the earliest value through that date. An open side needs a range filter on that field in the workbook, since tabpull reads its endpoint from there. A filter with no sheet is applied on the first sheet in the job, and tabpull prints that. With one configured site, `--site` can be omitted. With several, pass `--site` or pick one at the prompt.
|
|
98
|
+
|
|
99
|
+
`run` keeps going when one job fails. `--parallel N` exports that many jobs at once (default 1, so a run that does not ask for more still goes one job at a time). Above 1, each of those N slots runs in its own process with one browser it reuses for every job it takes, each job in a new context signed in from that site's saved session, and two jobs whose paths are one file on the output disk are refused before anything is exported. A terminal shows a progress bar and a summary panel titled `done: <ok>/<total> jobs exported`. A pipe prints that same `done` line and a check or cross per job. If any job failed, it prints the `tabpull run` command that reruns only those and exits 1. Each job uses the site it names. When that site's SSO session is missing or expired, tabpull opens the sign-in window once at a terminal before that site's jobs start, and otherwise exits and tells you to run `tabpull login --site <name>`. With `--parallel` above 1, a job that finds the session missing after that check fails with that same login line. Crosstab CSVs are rewritten from Tableau's UTF-16 tab-separated format to UTF-8 CSV.
|
|
100
|
+
|
|
101
|
+
`run --filter` uses the same repeatable syntax as `add`. Each override applies to every job in that run and is not written back to the jobs file. With no ` @Sheet` it replaces the job's saved filter on that field on every sheet. With ` @Sheet` it replaces only that sheet's. A field the job does not already filter is added on ` @Sheet`, or with no sheet on the first sheet, and tabpull prints that. Leave one side of `min..max` empty when that end should stay the filter's own limit. Relative dates are refused, so compute them in the caller:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
start=2026-09-01
|
|
105
|
+
end=$(date +%F)
|
|
106
|
+
tabpull run daily-west \
|
|
107
|
+
--filter "Order Date=${start}.." \
|
|
108
|
+
--out "./exports/${end}"
|
|
109
|
+
tabpull run daily-west weekly-east \
|
|
110
|
+
--filter "Order Date=${start}..${end}" \
|
|
111
|
+
--out "./exports/${end}"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`run --param` uses the same repeatable Name=value syntax as `add`. Each override applies to every job in that run and is not written back to the jobs file. A saved param of that name is replaced. A name the job does not have is added for this run. `tabpull run daily-west --param "Top N=10"`.
|
|
115
|
+
|
|
116
|
+
`run` writes `<job>/<sheet>.csv` under the current folder (spaces in names become `_`). `--jobs` and `--out` work before or after the subcommand:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
tabpull run daily-west --jobs ./jobs.toml --out ./exports
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Where files live
|
|
123
|
+
|
|
124
|
+
Tokens, cookies, and jobs stay in the config directory. Exports go to the working directory, or `--out`. Deleting the config directory removes tabpull's settings. A `.env` file and `TABLEAU_*` environment variables are not read.
|
|
125
|
+
|
|
126
|
+
| | Linux | macOS | Windows |
|
|
127
|
+
| --- | --- | --- | --- |
|
|
128
|
+
| Config (site tokens, SSO cookies, `jobs.toml`) | `$XDG_CONFIG_HOME/tabpull` or `~/.config/tabpull` | `~/Library/Application Support/tabpull` | `%APPDATA%\tabpull` |
|
|
129
|
+
|
|
130
|
+
`XDG_CONFIG_HOME` wins on every OS when it is set. `<config>` below is that directory.
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
<config>/sites/<name>.env server, Tableau site, token name, token secret
|
|
134
|
+
<config>/jobs.toml
|
|
135
|
+
<config>/auth/<name>.json SSO cookies; mode 600
|
|
136
|
+
./<job>/<sheet>.csv exports, relative to where you ran tabpull
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The token secret is not printed. Setup and login print the server and the Tableau site.
|
|
140
|
+
|
|
141
|
+
## Jobs file
|
|
142
|
+
|
|
143
|
+
`add` writes this. `remove` and edits on the interactive screen rewrite it from the saved jobs, which drops comments. You can edit the file by hand:
|
|
144
|
+
|
|
145
|
+
```toml
|
|
146
|
+
[[job]]
|
|
147
|
+
name = "daily-west"
|
|
148
|
+
site = "finance" # local name from `tabpull setup`
|
|
149
|
+
view = "SalesWorkbook/Overview" # Workbook/View from the URL
|
|
150
|
+
sheets = ["Order Detail", "Totals"]
|
|
151
|
+
filters = [
|
|
152
|
+
{ field = "Region", values = ["West", "Central"] },
|
|
153
|
+
{ field = "Order Date", min = "2026-09-01", max = "2026-09-25", sheet = "Totals" },
|
|
154
|
+
]
|
|
155
|
+
params = { "Top N" = "25" }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Parameters are set first, then filters. A filter applies on `sheet`. When `sheet` is omitted, tabpull applies that filter on the first entry in `sheets` and prints a line saying so. It does not copy the filter onto every sheet. Other sheets update only as they would in the dashboard when that filter changes.
|
|
159
|
+
|
|
160
|
+
`YYYY-MM-DD`, `M/D/YYYY`, and plain numbers are converted for range filters. An impossible date such as `2024-02-31` or `2/31/2024` is rejected. A relative date (`yesterday`, `last week`, `today`, `7 days ago`) or a date computed at run time is refused. Use a parameter, or write an absolute `YYYY-MM-DD` or `M/D/YYYY` date. Omit `min` or `max` to leave that end open: `{ field = "Order Date", min = "2026-09-01", sheet = "Totals" }` runs from that date through the latest value the filter allows, and `{ field = "Order Date", max = "2026-09-25", sheet = "Totals" }` runs from the earliest value through that date.
|
|
161
|
+
|
|
162
|
+
Stories are refused. Use the dashboard inside the story.
|
|
163
|
+
|
|
164
|
+
## Develop
|
|
165
|
+
|
|
166
|
+
From a checkout, `uv tool install .` installs the command, and `uv run tabpull` runs it without installing. `uv run playwright install chromium` installs the Chromium fallback for that checkout. An installed `tabpull` prints `uvx playwright==<version> install chromium` for the Playwright version it has.
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
uv run ruff check && uv run ruff format && uv run ty check && uv run pytest
|
|
170
|
+
```
|
tabpull-0.1.0/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/jer-castro/tabpull/main/assets/hero.svg" alt="tabpull" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
tabpull exports named worksheets from a published Tableau dashboard to UTF-8 CSV.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pip install tabpull
|
|
9
|
+
uv tool install tabpull
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
That installs the `tabpull` command. Bare `tabpull` at a terminal opens an interactive screen. Add, setup, login, and run stay on that screen. `setup`, `add`, `remove`, `run`, and `login` are also subcommands and work the same with or without it. Pin a release with `tabpull==X.Y.Z` or `uv tool install tabpull==X.Y.Z`. Upgrade a uv tool with `uv tool upgrade tabpull`. A commit that is not published yet still installs with `uv tool install git+https://github.com/jer-castro/tabpull`. Add `@vX.Y.Z` on that URL to pin a tag.
|
|
13
|
+
|
|
14
|
+
tabpull launches installed Chrome, then Edge. When neither is installed, install Chromium after the package. After `pip install tabpull`, run `playwright install chromium` in that environment. After `uv tool install tabpull`, the `playwright` command is not on `PATH`. Run `uvx playwright==<version> install chromium` with the version tabpull prints when Chrome and Edge are missing. That command uses the same browser revision and the default Playwright cache as the installed package.
|
|
15
|
+
|
|
16
|
+
## Why Embedding API
|
|
17
|
+
|
|
18
|
+
REST crosstab and data endpoints only export the first sheet when the view is a dashboard. A worksheet that lives only inside a published dashboard is not published as its own view, so it has no REST view.
|
|
19
|
+
|
|
20
|
+
Those sheets go through the Embedding API (`exportCrosstabAsync`) in a headless browser signed in with your SSO session. The personal access token is used to find views.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
tabpull setup
|
|
26
|
+
tabpull setup --site finance # local name jobs will use
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The wizard asks for that name (unless you passed `--site`), a dashboard URL, and a personal access token. It checks the token, then opens a browser for SSO.
|
|
30
|
+
|
|
31
|
+
Run it again for another server or site. Each site keeps its own token and browser session. Re-running a name keeps the current values when you press Enter.
|
|
32
|
+
|
|
33
|
+
The browser is installed Chrome, then Edge. If neither is installed, tabpull prints `uvx playwright==<version> install chromium` for the Playwright version it has. It does not download a browser while Chrome or Edge is already there.
|
|
34
|
+
|
|
35
|
+
## Use
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
tabpull # interactive screen at a terminal; a listing when piped
|
|
39
|
+
tabpull add # search a view or paste its URL, pick sheets and filters
|
|
40
|
+
tabpull add --site finance \
|
|
41
|
+
--view SalesWorkbook/Overview \
|
|
42
|
+
--sheet "Order Detail" --sheet Totals \
|
|
43
|
+
--filter "Region=West|Central" \
|
|
44
|
+
--filter "Order Date=2026-09-01..2026-09-25 @Totals" \
|
|
45
|
+
--param "Top N=25" \
|
|
46
|
+
--name daily-west
|
|
47
|
+
tabpull run # every job, into the current folder
|
|
48
|
+
tabpull run daily-west
|
|
49
|
+
tabpull remove daily-west # delete that saved job
|
|
50
|
+
tabpull remove # pick jobs to delete
|
|
51
|
+
tabpull login # refresh SSO for the only site
|
|
52
|
+
tabpull login --site finance
|
|
53
|
+
tabpull --version
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
With no command at a terminal, tabpull opens a full-screen list of sites and jobs. Open a job to change its filters (values or range bounds), parameters, sheets, view, site, and name. Adding a filter or parameter, changing which field a row uses, and choosing sheets pick from the live view when it opens, and from the typed forms when it cannot, with the reason shown. Editing a value that is already chosen stays a form and does not open the view. Each change is checked and saved to the jobs file right away. `r` runs the selected or marked jobs, `a` adds, `d` removes after a confirm, `s` and `l` run setup and login. Run, add, setup, and login happen on the screen; escape or ctrl+c cancels a running step. The footer lists the keys, `?` shows all of them, `q` quits.
|
|
57
|
+
|
|
58
|
+
With no command and no terminal (a pipe or a script), tabpull prints configured sites, saved jobs, the jobs file, the output folder, and the next commands in [TOON](https://toonformat.dev/). It never waits for keys. Errors, including an unknown flag, print `error: ...` on stdout. A usage error exits 2. Any other failure exits 1.
|
|
59
|
+
|
|
60
|
+
`add` saves the job and prints the `run` command. It does not export. With `--view` and at least one `--sheet`, it writes the job and does not prompt. Leave those flags off and it asks.
|
|
61
|
+
|
|
62
|
+
`remove` deletes the named jobs and rewrites the jobs file. With no names, a terminal asks which jobs to delete and confirms before writing. With no terminal, pass the names. An unknown name exits 1 and lists the saved jobs, and the file is left unchanged. Removing every job leaves the file empty.
|
|
63
|
+
|
|
64
|
+
`add` with `--view` and `--sheet` still opens the view far enough to refuse a story: `Stories are not supported; use the dashboard inside it.` `run` refuses a story the same way, before it exports.
|
|
65
|
+
|
|
66
|
+
`--filter` is `Field=a|b` or `Field=min..max`. ` @Sheet` names the worksheet. Either side of a range may be omitted, but not both: `Field=min..` runs from that date through the latest value the filter allows, and `Field=..max` runs from the earliest value through that date. An open side needs a range filter on that field in the workbook, since tabpull reads its endpoint from there. A filter with no sheet is applied on the first sheet in the job, and tabpull prints that. With one configured site, `--site` can be omitted. With several, pass `--site` or pick one at the prompt.
|
|
67
|
+
|
|
68
|
+
`run` keeps going when one job fails. `--parallel N` exports that many jobs at once (default 1, so a run that does not ask for more still goes one job at a time). Above 1, each of those N slots runs in its own process with one browser it reuses for every job it takes, each job in a new context signed in from that site's saved session, and two jobs whose paths are one file on the output disk are refused before anything is exported. A terminal shows a progress bar and a summary panel titled `done: <ok>/<total> jobs exported`. A pipe prints that same `done` line and a check or cross per job. If any job failed, it prints the `tabpull run` command that reruns only those and exits 1. Each job uses the site it names. When that site's SSO session is missing or expired, tabpull opens the sign-in window once at a terminal before that site's jobs start, and otherwise exits and tells you to run `tabpull login --site <name>`. With `--parallel` above 1, a job that finds the session missing after that check fails with that same login line. Crosstab CSVs are rewritten from Tableau's UTF-16 tab-separated format to UTF-8 CSV.
|
|
69
|
+
|
|
70
|
+
`run --filter` uses the same repeatable syntax as `add`. Each override applies to every job in that run and is not written back to the jobs file. With no ` @Sheet` it replaces the job's saved filter on that field on every sheet. With ` @Sheet` it replaces only that sheet's. A field the job does not already filter is added on ` @Sheet`, or with no sheet on the first sheet, and tabpull prints that. Leave one side of `min..max` empty when that end should stay the filter's own limit. Relative dates are refused, so compute them in the caller:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
start=2026-09-01
|
|
74
|
+
end=$(date +%F)
|
|
75
|
+
tabpull run daily-west \
|
|
76
|
+
--filter "Order Date=${start}.." \
|
|
77
|
+
--out "./exports/${end}"
|
|
78
|
+
tabpull run daily-west weekly-east \
|
|
79
|
+
--filter "Order Date=${start}..${end}" \
|
|
80
|
+
--out "./exports/${end}"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`run --param` uses the same repeatable Name=value syntax as `add`. Each override applies to every job in that run and is not written back to the jobs file. A saved param of that name is replaced. A name the job does not have is added for this run. `tabpull run daily-west --param "Top N=10"`.
|
|
84
|
+
|
|
85
|
+
`run` writes `<job>/<sheet>.csv` under the current folder (spaces in names become `_`). `--jobs` and `--out` work before or after the subcommand:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
tabpull run daily-west --jobs ./jobs.toml --out ./exports
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Where files live
|
|
92
|
+
|
|
93
|
+
Tokens, cookies, and jobs stay in the config directory. Exports go to the working directory, or `--out`. Deleting the config directory removes tabpull's settings. A `.env` file and `TABLEAU_*` environment variables are not read.
|
|
94
|
+
|
|
95
|
+
| | Linux | macOS | Windows |
|
|
96
|
+
| --- | --- | --- | --- |
|
|
97
|
+
| Config (site tokens, SSO cookies, `jobs.toml`) | `$XDG_CONFIG_HOME/tabpull` or `~/.config/tabpull` | `~/Library/Application Support/tabpull` | `%APPDATA%\tabpull` |
|
|
98
|
+
|
|
99
|
+
`XDG_CONFIG_HOME` wins on every OS when it is set. `<config>` below is that directory.
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
<config>/sites/<name>.env server, Tableau site, token name, token secret
|
|
103
|
+
<config>/jobs.toml
|
|
104
|
+
<config>/auth/<name>.json SSO cookies; mode 600
|
|
105
|
+
./<job>/<sheet>.csv exports, relative to where you ran tabpull
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The token secret is not printed. Setup and login print the server and the Tableau site.
|
|
109
|
+
|
|
110
|
+
## Jobs file
|
|
111
|
+
|
|
112
|
+
`add` writes this. `remove` and edits on the interactive screen rewrite it from the saved jobs, which drops comments. You can edit the file by hand:
|
|
113
|
+
|
|
114
|
+
```toml
|
|
115
|
+
[[job]]
|
|
116
|
+
name = "daily-west"
|
|
117
|
+
site = "finance" # local name from `tabpull setup`
|
|
118
|
+
view = "SalesWorkbook/Overview" # Workbook/View from the URL
|
|
119
|
+
sheets = ["Order Detail", "Totals"]
|
|
120
|
+
filters = [
|
|
121
|
+
{ field = "Region", values = ["West", "Central"] },
|
|
122
|
+
{ field = "Order Date", min = "2026-09-01", max = "2026-09-25", sheet = "Totals" },
|
|
123
|
+
]
|
|
124
|
+
params = { "Top N" = "25" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Parameters are set first, then filters. A filter applies on `sheet`. When `sheet` is omitted, tabpull applies that filter on the first entry in `sheets` and prints a line saying so. It does not copy the filter onto every sheet. Other sheets update only as they would in the dashboard when that filter changes.
|
|
128
|
+
|
|
129
|
+
`YYYY-MM-DD`, `M/D/YYYY`, and plain numbers are converted for range filters. An impossible date such as `2024-02-31` or `2/31/2024` is rejected. A relative date (`yesterday`, `last week`, `today`, `7 days ago`) or a date computed at run time is refused. Use a parameter, or write an absolute `YYYY-MM-DD` or `M/D/YYYY` date. Omit `min` or `max` to leave that end open: `{ field = "Order Date", min = "2026-09-01", sheet = "Totals" }` runs from that date through the latest value the filter allows, and `{ field = "Order Date", max = "2026-09-25", sheet = "Totals" }` runs from the earliest value through that date.
|
|
130
|
+
|
|
131
|
+
Stories are refused. Use the dashboard inside the story.
|
|
132
|
+
|
|
133
|
+
## Develop
|
|
134
|
+
|
|
135
|
+
From a checkout, `uv tool install .` installs the command, and `uv run tabpull` runs it without installing. `uv run playwright install chromium` installs the Chromium fallback for that checkout. An installed `tabpull` prints `uvx playwright==<version> install chromium` for the Playwright version it has.
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
uv run ruff check && uv run ruff format && uv run ty check && uv run pytest
|
|
139
|
+
```
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tabpull"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Export Tableau dashboard sheet crosstabs with filters"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "Jeremy Castro", email = "160854558+jer-castro@users.noreply.github.com" }]
|
|
9
|
+
keywords = ["tableau", "crosstab", "csv", "cli"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Environment :: Console",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Intended Audience :: End Users/Desktop",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Utilities",
|
|
21
|
+
]
|
|
22
|
+
requires-python = ">=3.12"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"playwright==1.63.0",
|
|
25
|
+
"questionary==2.1.1",
|
|
26
|
+
"rich==15.0.0",
|
|
27
|
+
"tableauserverclient==0.41",
|
|
28
|
+
"textual==8.2.8",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/jer-castro/tabpull"
|
|
33
|
+
Repository = "https://github.com/jer-castro/tabpull"
|
|
34
|
+
Issues = "https://github.com/jer-castro/tabpull/issues"
|
|
35
|
+
Changelog = "https://github.com/jer-castro/tabpull/blob/main/CHANGELOG.md"
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
tabpull = "tabpull.cli:main"
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["setuptools>=77"]
|
|
42
|
+
build-backend = "setuptools.build_meta"
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.packages.find]
|
|
45
|
+
where = ["src"]
|
|
46
|
+
include = ["tabpull*"]
|
|
47
|
+
|
|
48
|
+
[tool.uv]
|
|
49
|
+
|
|
50
|
+
package = true
|
|
51
|
+
|
|
52
|
+
python-preference = "only-managed"
|
|
53
|
+
exclude-newer = "7 days"
|
|
54
|
+
constraint-dependencies = ["litellm!=1.82.7,!=1.82.8,!=1.107.0"]
|
|
55
|
+
add-bounds = "exact"
|
|
56
|
+
|
|
57
|
+
[dependency-groups]
|
|
58
|
+
|
|
59
|
+
dev = ["pytest==9.1.1", "ruff==0.15.20", "ty==0.0.59"]
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
ignore = [
|
|
63
|
+
"line-too-long",
|
|
64
|
+
"module-import-not-at-top-of-file",
|
|
65
|
+
"blanket-noqa",
|
|
66
|
+
"empty-docstring",
|
|
67
|
+
]
|
|
68
|
+
extend-select = [
|
|
69
|
+
"E",
|
|
70
|
+
"W",
|
|
71
|
+
"F",
|
|
72
|
+
"I",
|
|
73
|
+
"UP",
|
|
74
|
+
"B",
|
|
75
|
+
"SIM",
|
|
76
|
+
"C4",
|
|
77
|
+
"PIE",
|
|
78
|
+
"LOG",
|
|
79
|
+
"N",
|
|
80
|
+
"ARG",
|
|
81
|
+
"PT",
|
|
82
|
+
"PGH",
|
|
83
|
+
"PLE",
|
|
84
|
+
"implicit-namespace-package",
|
|
85
|
+
"asyncio-dangling-task",
|
|
86
|
+
"suspicious-eval-usage",
|
|
87
|
+
"subprocess-popen-with-shell-equals-true",
|
|
88
|
+
"hardcoded-sql-expression",
|
|
89
|
+
"unsafe-yaml-load",
|
|
90
|
+
"suspicious-pickle-usage",
|
|
91
|
+
"hashlib-insecure-hash-function",
|
|
92
|
+
"request-without-timeout",
|
|
93
|
+
]
|
|
94
|
+
preview = true
|
|
95
|
+
unfixable = ["unused-import"]
|
|
96
|
+
|
|
97
|
+
[tool.ruff.format]
|
|
98
|
+
quote-style = "single"
|
|
99
|
+
|
|
100
|
+
[tool.ruff.lint.per-file-ignores]
|
|
101
|
+
"test_*.py" = ["ARG", "implicit-namespace-package"]
|
|
102
|
+
"tests/**/conftest.py" = ["ARG", "implicit-namespace-package"]
|
|
103
|
+
|
|
104
|
+
[tool.ty.rules]
|
|
105
|
+
missing-type-argument = "error"
|
|
106
|
+
unknown-argument = "warn"
|
|
107
|
+
redundant-cast = "ignore"
|
|
108
|
+
|
|
109
|
+
[tool.ty.environment]
|
|
110
|
+
extra-paths = ["src"]
|
|
111
|
+
|
|
112
|
+
[tool.pytest.ini_options]
|
|
113
|
+
pythonpath = ["src"]
|
|
114
|
+
testpaths = ["tests"]
|
tabpull-0.1.0/setup.cfg
ADDED
|
File without changes
|