pytableau 0.1.0__py3-none-any.whl
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.
- pytableau/__init__.py +93 -0
- pytableau/_compat.py +46 -0
- pytableau/_version.py +1 -0
- pytableau/cli/__init__.py +15 -0
- pytableau/cli/main.py +137 -0
- pytableau/constants.py +212 -0
- pytableau/core/__init__.py +38 -0
- pytableau/core/dashboard.py +337 -0
- pytableau/core/datasource.py +470 -0
- pytableau/core/fields.py +398 -0
- pytableau/core/filters.py +331 -0
- pytableau/core/formatting.py +7 -0
- pytableau/core/workbook.py +352 -0
- pytableau/core/worksheet.py +464 -0
- pytableau/data/__init__.py +23 -0
- pytableau/data/bridge.py +157 -0
- pytableau/data/extract.py +114 -0
- pytableau/data/types.py +81 -0
- pytableau/exceptions.py +144 -0
- pytableau/inspect/__init__.py +19 -0
- pytableau/inspect/catalog.py +100 -0
- pytableau/inspect/diff.py +7 -0
- pytableau/inspect/lineage.py +121 -0
- pytableau/inspect/report.py +119 -0
- pytableau/package/__init__.py +12 -0
- pytableau/package/assets.py +7 -0
- pytableau/package/manager.py +146 -0
- pytableau/py.typed +0 -0
- pytableau/server/__init__.py +13 -0
- pytableau/server/client.py +174 -0
- pytableau/server/workflows.py +67 -0
- pytableau/templates/__init__.py +24 -0
- pytableau/templates/engine.py +131 -0
- pytableau/templates/library/__init__.py +58 -0
- pytableau/templates/library/bar_chart.twb +28 -0
- pytableau/templates/library/heatmap.twb +29 -0
- pytableau/templates/library/kpi_dashboard.twb +25 -0
- pytableau/templates/library/line_chart.twb +28 -0
- pytableau/templates/library/map.twb +28 -0
- pytableau/templates/library/scatter_plot.twb +29 -0
- pytableau/templates/library/treemap.twb +29 -0
- pytableau/templates/mapping.py +65 -0
- pytableau/xml/__init__.py +7 -0
- pytableau/xml/differ.py +7 -0
- pytableau/xml/discovery/__init__.py +11 -0
- pytableau/xml/discovery/controlled_diff.py +43 -0
- pytableau/xml/discovery/corpus.py +53 -0
- pytableau/xml/engine.py +130 -0
- pytableau/xml/proxy.py +37 -0
- pytableau/xml/schemas/__init__.py +11 -0
- pytableau/xml/schemas/base.py +7 -0
- pytableau/xml/schemas/v2022.py +7 -0
- pytableau/xml/schemas/v2023.py +7 -0
- pytableau/xml/schemas/v2024.py +7 -0
- pytableau/xml/schemas/v2025.py +7 -0
- pytableau/xml/writer.py +7 -0
- pytableau-0.1.0.dist-info/METADATA +177 -0
- pytableau-0.1.0.dist-info/RECORD +61 -0
- pytableau-0.1.0.dist-info/WHEEL +4 -0
- pytableau-0.1.0.dist-info/entry_points.txt +2 -0
- pytableau-0.1.0.dist-info/licenses/LICENSE +21 -0
pytableau/__init__.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""pytableau — The unified Python SDK for Tableau workbook engineering.
|
|
2
|
+
|
|
3
|
+
Quickstart::
|
|
4
|
+
|
|
5
|
+
from pytableau import Workbook
|
|
6
|
+
|
|
7
|
+
wb = Workbook.open("sales_dashboard.twbx")
|
|
8
|
+
print(wb.version)
|
|
9
|
+
wb.save_as("modified.twbx")
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from pytableau._version import __version__
|
|
15
|
+
from pytableau.constants import (
|
|
16
|
+
AggregationType,
|
|
17
|
+
ConnectionType,
|
|
18
|
+
DataType,
|
|
19
|
+
FilterType,
|
|
20
|
+
MarkType,
|
|
21
|
+
ParameterDomainType,
|
|
22
|
+
Role,
|
|
23
|
+
SortOrder,
|
|
24
|
+
ValidationLevel,
|
|
25
|
+
)
|
|
26
|
+
from pytableau.core.workbook import Workbook
|
|
27
|
+
from pytableau.exceptions import (
|
|
28
|
+
AuthenticationError,
|
|
29
|
+
ConnectionError,
|
|
30
|
+
CorruptWorkbookError,
|
|
31
|
+
DatasourceError,
|
|
32
|
+
DatasourceNotFoundError,
|
|
33
|
+
DuplicateFieldError,
|
|
34
|
+
ExtractError,
|
|
35
|
+
FieldError,
|
|
36
|
+
FieldNotFoundError,
|
|
37
|
+
FileError,
|
|
38
|
+
FormulaError,
|
|
39
|
+
HyperError,
|
|
40
|
+
IncompatibleVersionError,
|
|
41
|
+
InvalidWorkbookError,
|
|
42
|
+
PackageError,
|
|
43
|
+
PublishError,
|
|
44
|
+
PyTableauError,
|
|
45
|
+
SchemaValidationError,
|
|
46
|
+
ServerError,
|
|
47
|
+
TemplateError,
|
|
48
|
+
TemplateNotFoundError,
|
|
49
|
+
UnmappedPlaceholderError,
|
|
50
|
+
ValidationIssue,
|
|
51
|
+
XMLError,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
# Version
|
|
56
|
+
"__version__",
|
|
57
|
+
# Top-level exceptions
|
|
58
|
+
"PyTableauError",
|
|
59
|
+
"FileError",
|
|
60
|
+
"InvalidWorkbookError",
|
|
61
|
+
"PackageError",
|
|
62
|
+
"CorruptWorkbookError",
|
|
63
|
+
"XMLError",
|
|
64
|
+
"SchemaValidationError",
|
|
65
|
+
"IncompatibleVersionError",
|
|
66
|
+
"FieldError",
|
|
67
|
+
"FieldNotFoundError",
|
|
68
|
+
"DuplicateFieldError",
|
|
69
|
+
"FormulaError",
|
|
70
|
+
"DatasourceError",
|
|
71
|
+
"DatasourceNotFoundError",
|
|
72
|
+
"ConnectionError",
|
|
73
|
+
"HyperError",
|
|
74
|
+
"ExtractError",
|
|
75
|
+
"ServerError",
|
|
76
|
+
"AuthenticationError",
|
|
77
|
+
"PublishError",
|
|
78
|
+
"TemplateError",
|
|
79
|
+
"UnmappedPlaceholderError",
|
|
80
|
+
"TemplateNotFoundError",
|
|
81
|
+
"ValidationIssue",
|
|
82
|
+
"Workbook",
|
|
83
|
+
# Enums
|
|
84
|
+
"MarkType",
|
|
85
|
+
"DataType",
|
|
86
|
+
"Role",
|
|
87
|
+
"AggregationType",
|
|
88
|
+
"FilterType",
|
|
89
|
+
"SortOrder",
|
|
90
|
+
"ConnectionType",
|
|
91
|
+
"ParameterDomainType",
|
|
92
|
+
"ValidationLevel",
|
|
93
|
+
]
|
pytableau/_compat.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Optional dependency import helpers.
|
|
2
|
+
|
|
3
|
+
Provides clear error messages when optional extras are not installed.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import importlib
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _MissingDependency:
|
|
13
|
+
"""Placeholder that raises ImportError on any attribute access."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, name: str, extra: str) -> None:
|
|
16
|
+
self._name = name
|
|
17
|
+
self._extra = extra
|
|
18
|
+
|
|
19
|
+
def __getattr__(self, attr: str) -> Any:
|
|
20
|
+
raise ImportError(
|
|
21
|
+
f"{self._name} is required for this feature. "
|
|
22
|
+
f"Install it with: pip install pytableau[{self._extra}]"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
|
26
|
+
raise ImportError(
|
|
27
|
+
f"{self._name} is required for this feature. "
|
|
28
|
+
f"Install it with: pip install pytableau[{self._extra}]"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def import_optional(module_name: str, extra: str) -> Any:
|
|
33
|
+
"""Import an optional dependency, returning a stub if unavailable.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
module_name: Fully qualified module name (e.g. ``"tableauhyperapi"``).
|
|
37
|
+
extra: The pip extra that provides this dependency (e.g. ``"hyper"``).
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
The imported module, or a :class:`_MissingDependency` stub that raises
|
|
41
|
+
a helpful :class:`ImportError` on first use.
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
return importlib.import_module(module_name)
|
|
45
|
+
except ImportError:
|
|
46
|
+
return _MissingDependency(module_name, extra)
|
pytableau/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Optional CLI interface for pytableau.
|
|
2
|
+
|
|
3
|
+
Provides the ``pytableau`` command-line tool.
|
|
4
|
+
|
|
5
|
+
Usage::
|
|
6
|
+
|
|
7
|
+
pytableau inspect workbook.twbx
|
|
8
|
+
pytableau diff before.twb after.twb
|
|
9
|
+
pytableau swap workbook.twb --server prod-db.corp.com --db analytics_prod
|
|
10
|
+
|
|
11
|
+
.. note::
|
|
12
|
+
Full implementation is tracked in Phase 1 of the development plan.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
pytableau/cli/main.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""CLI entry point: ``pytableau inspect``, ``pytableau diff``, ``pytableau swap``.
|
|
2
|
+
|
|
3
|
+
The CLI is built with `click <https://click.palletsprojects.com/>`_.
|
|
4
|
+
|
|
5
|
+
.. note::
|
|
6
|
+
Full implementation is tracked in Phase 1 of the development plan.
|
|
7
|
+
Phase 0 ships a minimal skeleton so that the ``pytableau`` console
|
|
8
|
+
script entry point is importable.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
import click
|
|
17
|
+
except ImportError as exc:
|
|
18
|
+
raise ImportError(
|
|
19
|
+
"The pytableau CLI requires 'click'. "
|
|
20
|
+
"Install it with: pip install click"
|
|
21
|
+
) from exc
|
|
22
|
+
|
|
23
|
+
from pytableau.core.workbook import Workbook
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.group()
|
|
27
|
+
@click.version_option(package_name="pytableau")
|
|
28
|
+
def app() -> None:
|
|
29
|
+
"""pytableau — The unified Python SDK for Tableau workbook engineering."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@app.command()
|
|
33
|
+
@click.argument("workbook", type=click.Path(exists=True))
|
|
34
|
+
@click.option("--format", "fmt", default="table", type=click.Choice(["table", "json"]))
|
|
35
|
+
def inspect(workbook: str, fmt: str) -> None:
|
|
36
|
+
"""Inspect a Tableau workbook and print its contents."""
|
|
37
|
+
wb = Workbook.open(workbook)
|
|
38
|
+
payload = {
|
|
39
|
+
"path": str(workbook),
|
|
40
|
+
"version": wb.version,
|
|
41
|
+
"source_platform": wb.source_platform,
|
|
42
|
+
"datasource_count": len(wb.datasources),
|
|
43
|
+
"datasources": wb.datasources.names,
|
|
44
|
+
"worksheet_count": len(wb.worksheets),
|
|
45
|
+
"worksheets": wb.worksheets.names,
|
|
46
|
+
"dashboard_count": len(wb.dashboards),
|
|
47
|
+
"dashboards": wb.dashboards.names,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if fmt == "json":
|
|
51
|
+
click.echo(json.dumps(payload, indent=2, sort_keys=True))
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
click.echo(f"Workbook: {workbook}")
|
|
55
|
+
click.echo(f"Version: {wb.version}")
|
|
56
|
+
if wb.source_platform:
|
|
57
|
+
click.echo(f"Source platform: {wb.source_platform}")
|
|
58
|
+
click.echo(f"Datasources ({payload['datasource_count']}):")
|
|
59
|
+
for name in payload["datasources"]:
|
|
60
|
+
click.echo(f" - {name}")
|
|
61
|
+
click.echo(f"Worksheets ({payload['worksheet_count']}):")
|
|
62
|
+
for name in payload["worksheets"]:
|
|
63
|
+
click.echo(f" - {name}")
|
|
64
|
+
click.echo(f"Dashboards ({payload['dashboard_count']}):")
|
|
65
|
+
for name in payload["dashboards"]:
|
|
66
|
+
click.echo(f" - {name}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@app.command()
|
|
70
|
+
@click.argument("before", type=click.Path(exists=True))
|
|
71
|
+
@click.argument("after", type=click.Path(exists=True))
|
|
72
|
+
def diff(before: str, after: str) -> None:
|
|
73
|
+
"""Show semantic differences between two Tableau workbooks."""
|
|
74
|
+
before_wb = Workbook.open(before)
|
|
75
|
+
after_wb = Workbook.open(after)
|
|
76
|
+
|
|
77
|
+
before_xml = before_wb.to_xml_string().splitlines()
|
|
78
|
+
after_xml = after_wb.to_xml_string().splitlines()
|
|
79
|
+
|
|
80
|
+
if before_xml == after_xml:
|
|
81
|
+
click.echo("No differences detected.")
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
before_set = {line for line in before_xml}
|
|
85
|
+
after_set = {line for line in after_xml}
|
|
86
|
+
added = sorted(after_set - before_set)
|
|
87
|
+
removed = sorted(before_set - after_set)
|
|
88
|
+
|
|
89
|
+
click.echo(f"Added lines: {len(added)}")
|
|
90
|
+
for item in added[:40]:
|
|
91
|
+
click.echo(f" + {item}")
|
|
92
|
+
if len(added) > 40:
|
|
93
|
+
click.echo(f" ... ({len(added) - 40} more)")
|
|
94
|
+
|
|
95
|
+
click.echo(f"Removed lines: {len(removed)}")
|
|
96
|
+
for item in removed[:40]:
|
|
97
|
+
click.echo(f" - {item}")
|
|
98
|
+
if len(removed) > 40:
|
|
99
|
+
click.echo(f" ... ({len(removed) - 40} more)")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@app.command()
|
|
103
|
+
@click.argument("workbook", type=click.Path(exists=True))
|
|
104
|
+
@click.option("--server", required=True, help="New database server hostname")
|
|
105
|
+
@click.option("--db", required=False, help="New database name")
|
|
106
|
+
@click.option("--username", required=False, help="New database username")
|
|
107
|
+
@click.option("--output", "-o", required=False, help="Output path (default: overwrite)")
|
|
108
|
+
def swap(
|
|
109
|
+
workbook: str,
|
|
110
|
+
server: str,
|
|
111
|
+
db: str | None,
|
|
112
|
+
username: str | None,
|
|
113
|
+
output: str | None,
|
|
114
|
+
) -> None:
|
|
115
|
+
"""Swap connection properties in a Tableau workbook."""
|
|
116
|
+
wb = Workbook.open(workbook)
|
|
117
|
+
updated = 0
|
|
118
|
+
|
|
119
|
+
for conn in wb.xml_tree.findall(".//connection"):
|
|
120
|
+
if server:
|
|
121
|
+
conn.set("server", server)
|
|
122
|
+
updated += 1
|
|
123
|
+
if db:
|
|
124
|
+
if "dbname" in conn.attrib:
|
|
125
|
+
conn.set("dbname", db)
|
|
126
|
+
elif "dbName" in conn.attrib:
|
|
127
|
+
conn.set("dbName", db)
|
|
128
|
+
else:
|
|
129
|
+
conn.set("dbname", db)
|
|
130
|
+
updated += 1
|
|
131
|
+
if username:
|
|
132
|
+
conn.set("username", username)
|
|
133
|
+
updated += 1
|
|
134
|
+
|
|
135
|
+
destination = output or workbook
|
|
136
|
+
wb.save_as(destination)
|
|
137
|
+
click.echo(f"Updated {updated} connection attributes.")
|
pytableau/constants.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""Constants and enumerations for pytableau.
|
|
2
|
+
|
|
3
|
+
All Tableau-level concepts (mark types, data types, roles, etc.) are
|
|
4
|
+
represented as ``str`` enums so that values compare equal to the raw
|
|
5
|
+
XML attribute strings used by Tableau.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from enum import Enum
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MarkType(str, Enum):
|
|
14
|
+
"""Viz mark types as they appear in Tableau XML."""
|
|
15
|
+
|
|
16
|
+
AUTOMATIC = "automatic"
|
|
17
|
+
BAR = "bar"
|
|
18
|
+
LINE = "line"
|
|
19
|
+
AREA = "area"
|
|
20
|
+
CIRCLE = "circle"
|
|
21
|
+
SQUARE = "square"
|
|
22
|
+
SHAPE = "shape" # custom shapes palette
|
|
23
|
+
TEXT = "text"
|
|
24
|
+
MAP = "map"
|
|
25
|
+
PIE = "pie"
|
|
26
|
+
GANTT = "gantt_rounded"
|
|
27
|
+
GANTT_BAR = "gantt_rounded" # alias — same XML value
|
|
28
|
+
POLYGON = "polygon"
|
|
29
|
+
DENSITY = "density"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DataType(str, Enum):
|
|
33
|
+
"""Field data types as they appear in Tableau XML."""
|
|
34
|
+
|
|
35
|
+
STRING = "string"
|
|
36
|
+
INTEGER = "integer"
|
|
37
|
+
REAL = "real"
|
|
38
|
+
BOOLEAN = "boolean"
|
|
39
|
+
DATE = "date"
|
|
40
|
+
DATETIME = "datetime"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Role(str, Enum):
|
|
44
|
+
"""Semantic role of a field."""
|
|
45
|
+
|
|
46
|
+
DIMENSION = "dimension"
|
|
47
|
+
MEASURE = "measure"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AggregationType(str, Enum):
|
|
51
|
+
"""Default aggregation types for measure fields."""
|
|
52
|
+
|
|
53
|
+
SUM = "Sum"
|
|
54
|
+
AVG = "Avg"
|
|
55
|
+
COUNT = "Count"
|
|
56
|
+
COUNTD = "CountD"
|
|
57
|
+
MIN = "Min"
|
|
58
|
+
MAX = "Max"
|
|
59
|
+
MEDIAN = "Median"
|
|
60
|
+
ATTR = "Attr"
|
|
61
|
+
STDEV = "Stdev"
|
|
62
|
+
VAR = "Var"
|
|
63
|
+
YEAR = "Year"
|
|
64
|
+
QUARTER = "Quarter"
|
|
65
|
+
MONTH = "Month"
|
|
66
|
+
WEEK = "Week"
|
|
67
|
+
DAY = "Day"
|
|
68
|
+
NONE = "None"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class FilterType(str, Enum):
|
|
72
|
+
"""Filter types as they appear in Tableau XML."""
|
|
73
|
+
|
|
74
|
+
CATEGORICAL = "categorical"
|
|
75
|
+
QUANTITATIVE = "quantitative"
|
|
76
|
+
RELATIVE_DATE = "relative-date"
|
|
77
|
+
RANGE = "range"
|
|
78
|
+
TOP = "top"
|
|
79
|
+
WILDCARD = "wildcard"
|
|
80
|
+
ALL = "all"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class SortOrder(str, Enum):
|
|
84
|
+
"""Sort direction options."""
|
|
85
|
+
|
|
86
|
+
ASCENDING = "asc"
|
|
87
|
+
DESCENDING = "desc"
|
|
88
|
+
NONE = "none"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class SortType(str, Enum):
|
|
92
|
+
"""Sort method options."""
|
|
93
|
+
|
|
94
|
+
ALPHABETIC = "alphabetic"
|
|
95
|
+
DATA_SOURCE = "data-source-order"
|
|
96
|
+
FIELD = "field"
|
|
97
|
+
MANUAL = "manual"
|
|
98
|
+
NONE = "none"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ConnectionType(str, Enum):
|
|
102
|
+
"""Common Tableau connection class names (``class`` XML attribute)."""
|
|
103
|
+
|
|
104
|
+
HYPER = "hyper"
|
|
105
|
+
TEXTSCAN = "textscan" # CSV / text files
|
|
106
|
+
EXCEL = "excel-direct"
|
|
107
|
+
SQLSERVER = "sqlserver"
|
|
108
|
+
MSSQL = "sqlserver" # alias — same XML class value as SQLSERVER
|
|
109
|
+
POSTGRES = "postgres"
|
|
110
|
+
MYSQL = "mysql"
|
|
111
|
+
ORACLE = "oracle"
|
|
112
|
+
REDSHIFT = "redshift"
|
|
113
|
+
SNOWFLAKE = "snowflake"
|
|
114
|
+
BIGQUERY = "bigquery"
|
|
115
|
+
DATABRICKS = "spark"
|
|
116
|
+
ATHENA = "athena"
|
|
117
|
+
TABLEAU_SERVER = "tableau-server" # published data source on Tableau Server/Cloud
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class DashboardSizeType(str, Enum):
|
|
121
|
+
"""Dashboard size constraint modes."""
|
|
122
|
+
|
|
123
|
+
FIXED = "fixed"
|
|
124
|
+
AUTOMATIC = "automatic"
|
|
125
|
+
RANGE = "range"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class ZoneType(str, Enum):
|
|
129
|
+
"""Dashboard zone layout types."""
|
|
130
|
+
|
|
131
|
+
LAYOUT_BASIC = "layout-basic"
|
|
132
|
+
LAYOUT_FLOW = "layout-flow"
|
|
133
|
+
WORKSHEET = "worksheet"
|
|
134
|
+
TEXT = "text"
|
|
135
|
+
IMAGE = "image"
|
|
136
|
+
WEB = "web"
|
|
137
|
+
BLANK = "blank"
|
|
138
|
+
TITLE = "title"
|
|
139
|
+
LEGEND = "legend"
|
|
140
|
+
FILTER = "filter"
|
|
141
|
+
PARAMETER = "parameter"
|
|
142
|
+
EXTENSION = "extension"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ShelfType(str, Enum):
|
|
146
|
+
"""Worksheet shelf identifiers."""
|
|
147
|
+
|
|
148
|
+
ROWS = "rows"
|
|
149
|
+
COLUMNS = "columns"
|
|
150
|
+
COLOR = "color"
|
|
151
|
+
SIZE = "size"
|
|
152
|
+
LABEL = "label"
|
|
153
|
+
DETAIL = "detail"
|
|
154
|
+
TOOLTIP = "tooltip"
|
|
155
|
+
PATH = "path"
|
|
156
|
+
SHAPE = "shape"
|
|
157
|
+
ANGLE = "angle"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ActionType(str, Enum):
|
|
161
|
+
"""Dashboard action types."""
|
|
162
|
+
|
|
163
|
+
FILTER = "filter"
|
|
164
|
+
HIGHLIGHT = "highlight"
|
|
165
|
+
URL = "url"
|
|
166
|
+
GOTO_SHEET = "goto-sheet"
|
|
167
|
+
CHANGE_PARAMETER = "change-parameter"
|
|
168
|
+
CHANGE_SET_VALUES = "change-set-values"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ParameterDomainType(str, Enum):
|
|
172
|
+
"""Parameter allowable values type."""
|
|
173
|
+
|
|
174
|
+
ALL = "all"
|
|
175
|
+
LIST = "list"
|
|
176
|
+
RANGE = "range"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class ValidationLevel(str, Enum):
|
|
180
|
+
"""Severity levels for validation issues."""
|
|
181
|
+
|
|
182
|
+
ERROR = "error"
|
|
183
|
+
WARNING = "warning"
|
|
184
|
+
INFO = "info"
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
# Tableau version mapping: version string → XML source-build attribute value
|
|
189
|
+
# ---------------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
#: Maps human-readable Tableau version strings to the ``source-build``
|
|
192
|
+
#: attribute value written in workbook XML headers.
|
|
193
|
+
TABLEAU_VERSION_MAP: dict[str, str] = {
|
|
194
|
+
"2022.1": "20221.22.0418.1333",
|
|
195
|
+
"2022.2": "20222.22.0719.1543",
|
|
196
|
+
"2022.3": "20223.22.1007.0912",
|
|
197
|
+
"2022.4": "20224.23.0112.0803",
|
|
198
|
+
"2023.1": "20231.23.0309.0931",
|
|
199
|
+
"2023.2": "20232.23.0609.0906",
|
|
200
|
+
"2023.3": "20233.23.0921.0921",
|
|
201
|
+
"2023.4": "20234.24.0111.0824",
|
|
202
|
+
"2024.1": "20241.24.0312.0830",
|
|
203
|
+
"2024.2": "20242.24.0613.0835",
|
|
204
|
+
"2024.3": "20243.24.0912.0921",
|
|
205
|
+
"2025.1": "20251.25.0314.0930",
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#: The minimum Tableau version supported by pytableau.
|
|
209
|
+
MINIMUM_TABLEAU_VERSION = "2022.1"
|
|
210
|
+
|
|
211
|
+
#: The default Tableau version used when creating new workbooks.
|
|
212
|
+
DEFAULT_TABLEAU_VERSION = "2024.1"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Core object model for pytableau.
|
|
2
|
+
|
|
3
|
+
Provides the high-level Python objects that map to Tableau workbook
|
|
4
|
+
concepts: Workbook, Datasource, Worksheet, Dashboard, and field-like objects.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from .dashboard import Dashboard, DashboardCollection
|
|
10
|
+
from .datasource import Datasource, DatasourceCollection
|
|
11
|
+
from .fields import (
|
|
12
|
+
CalcFieldCollection,
|
|
13
|
+
CalculatedField,
|
|
14
|
+
Field,
|
|
15
|
+
FieldCollection,
|
|
16
|
+
FieldReference,
|
|
17
|
+
Parameter,
|
|
18
|
+
)
|
|
19
|
+
from .workbook import Workbook
|
|
20
|
+
from .worksheet import MarkCard, Shelf, Worksheet, WorksheetCollection
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Workbook",
|
|
24
|
+
"Datasource",
|
|
25
|
+
"DatasourceCollection",
|
|
26
|
+
"Dashboard",
|
|
27
|
+
"DashboardCollection",
|
|
28
|
+
"Worksheet",
|
|
29
|
+
"WorksheetCollection",
|
|
30
|
+
"Field",
|
|
31
|
+
"FieldCollection",
|
|
32
|
+
"CalcFieldCollection",
|
|
33
|
+
"CalculatedField",
|
|
34
|
+
"FieldReference",
|
|
35
|
+
"Parameter",
|
|
36
|
+
"MarkCard",
|
|
37
|
+
"Shelf",
|
|
38
|
+
]
|