tableauserverclient 0.33__py3-none-any.whl → 0.35__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.
- tableauserverclient/__init__.py +33 -23
- tableauserverclient/{_version.py → bin/_version.py} +3 -3
- tableauserverclient/config.py +5 -3
- tableauserverclient/models/column_item.py +1 -1
- tableauserverclient/models/connection_credentials.py +18 -2
- tableauserverclient/models/connection_item.py +44 -6
- tableauserverclient/models/custom_view_item.py +78 -11
- tableauserverclient/models/data_acceleration_report_item.py +2 -2
- tableauserverclient/models/data_alert_item.py +5 -5
- tableauserverclient/models/data_freshness_policy_item.py +6 -6
- tableauserverclient/models/database_item.py +3 -3
- tableauserverclient/models/datasource_item.py +10 -10
- tableauserverclient/models/dqw_item.py +1 -1
- tableauserverclient/models/favorites_item.py +5 -6
- tableauserverclient/models/fileupload_item.py +1 -1
- tableauserverclient/models/flow_item.py +54 -9
- tableauserverclient/models/flow_run_item.py +3 -3
- tableauserverclient/models/group_item.py +44 -4
- tableauserverclient/models/groupset_item.py +4 -4
- tableauserverclient/models/interval_item.py +9 -9
- tableauserverclient/models/job_item.py +73 -8
- tableauserverclient/models/linked_tasks_item.py +5 -5
- tableauserverclient/models/metric_item.py +5 -5
- tableauserverclient/models/pagination_item.py +1 -1
- tableauserverclient/models/permissions_item.py +12 -10
- tableauserverclient/models/project_item.py +73 -19
- tableauserverclient/models/property_decorators.py +12 -11
- tableauserverclient/models/reference_item.py +2 -2
- tableauserverclient/models/revision_item.py +3 -3
- tableauserverclient/models/schedule_item.py +2 -2
- tableauserverclient/models/server_info_item.py +26 -6
- tableauserverclient/models/site_item.py +69 -3
- tableauserverclient/models/subscription_item.py +3 -3
- tableauserverclient/models/table_item.py +1 -1
- tableauserverclient/models/tableau_auth.py +115 -5
- tableauserverclient/models/tableau_types.py +2 -2
- tableauserverclient/models/tag_item.py +3 -4
- tableauserverclient/models/task_item.py +34 -4
- tableauserverclient/models/user_item.py +47 -17
- tableauserverclient/models/view_item.py +66 -13
- tableauserverclient/models/virtual_connection_item.py +6 -5
- tableauserverclient/models/webhook_item.py +39 -6
- tableauserverclient/models/workbook_item.py +116 -13
- tableauserverclient/namespace.py +1 -1
- tableauserverclient/server/__init__.py +2 -1
- tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
- tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
- tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
- tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
- tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
- tableauserverclient/server/endpoint/datasources_endpoint.py +61 -62
- tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
- tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
- tableauserverclient/server/endpoint/endpoint.py +19 -21
- tableauserverclient/server/endpoint/exceptions.py +23 -7
- tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
- tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flows_endpoint.py +344 -29
- tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
- tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
- tableauserverclient/server/endpoint/jobs_endpoint.py +116 -7
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
- tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
- tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
- tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
- tableauserverclient/server/endpoint/projects_endpoint.py +681 -30
- tableauserverclient/server/endpoint/resource_tagger.py +14 -13
- tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
- tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
- tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
- tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
- tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
- tableauserverclient/server/endpoint/tasks_endpoint.py +86 -8
- tableauserverclient/server/endpoint/users_endpoint.py +366 -19
- tableauserverclient/server/endpoint/views_endpoint.py +262 -20
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
- tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
- tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
- tableauserverclient/server/filter.py +2 -2
- tableauserverclient/server/pager.py +29 -6
- tableauserverclient/server/query.py +68 -19
- tableauserverclient/server/request_factory.py +57 -37
- tableauserverclient/server/request_options.py +243 -141
- tableauserverclient/server/server.py +76 -10
- tableauserverclient/server/sort.py +16 -2
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
- tableauserverclient-0.35.dist-info/RECORD +106 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
- tableauserverclient-0.33.dist-info/RECORD +0 -106
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import sys
|
|
2
|
+
from typing import Optional
|
|
2
3
|
|
|
3
4
|
from typing_extensions import Self
|
|
4
5
|
|
|
@@ -9,12 +10,12 @@ import logging
|
|
|
9
10
|
from tableauserverclient.helpers.logging import logger
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
class RequestOptionsBase
|
|
13
|
+
class RequestOptionsBase:
|
|
13
14
|
# This method is used if server api version is below 3.7 (2020.1)
|
|
14
15
|
def apply_query_params(self, url):
|
|
15
16
|
try:
|
|
16
17
|
params = self.get_query_params()
|
|
17
|
-
params_list = ["{}={}"
|
|
18
|
+
params_list = [f"{k}={v}" for (k, v) in params.items()]
|
|
18
19
|
|
|
19
20
|
logger.debug("Applying options to request: <%s(%s)>", self.__class__.__name__, ",".join(params_list))
|
|
20
21
|
|
|
@@ -22,15 +23,74 @@ class RequestOptionsBase(object):
|
|
|
22
23
|
url, existing_params = url.split("?")
|
|
23
24
|
params_list.append(existing_params)
|
|
24
25
|
|
|
25
|
-
return "{
|
|
26
|
+
return "{}?{}".format(url, "&".join(params_list))
|
|
26
27
|
except NotImplementedError:
|
|
27
28
|
raise
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
# If it wasn't a breaking change, I'd rename it to QueryOptions
|
|
32
|
+
"""
|
|
33
|
+
This class manages options can be used when querying content on the server
|
|
34
|
+
"""
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
class RequestOptions(RequestOptionsBase):
|
|
38
|
+
"""
|
|
39
|
+
This class is used to manage the options that can be used when querying content on the server.
|
|
40
|
+
Optionally initialize with a page number and page size to control the number of items returned.
|
|
41
|
+
|
|
42
|
+
Additionally, you can add sorting and filtering options to the request.
|
|
43
|
+
|
|
44
|
+
The `sort` and `filter` options are set-like objects, so you can only add a field once. If you add the same field
|
|
45
|
+
multiple times, only the last one will be used.
|
|
46
|
+
|
|
47
|
+
The list of fields that can be sorted on or filtered by can be found in the `Field`
|
|
48
|
+
class contained within this class.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
pagenumber: int, optional
|
|
53
|
+
The page number to start the query on. Default is 1.
|
|
54
|
+
|
|
55
|
+
pagesize: int, optional
|
|
56
|
+
The number of items to return per page. Default is 100. Can also read
|
|
57
|
+
from the environment variable `TSC_PAGE_SIZE`
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, pagenumber=1, pagesize=None):
|
|
61
|
+
self.pagenumber = pagenumber
|
|
62
|
+
self.pagesize = pagesize or config.PAGE_SIZE
|
|
63
|
+
self.sort = set()
|
|
64
|
+
self.filter = set()
|
|
65
|
+
# This is private until we expand all of our parsers to handle the extra fields
|
|
66
|
+
self._all_fields = False
|
|
67
|
+
|
|
68
|
+
def get_query_params(self) -> dict:
|
|
69
|
+
params = {}
|
|
70
|
+
if self.sort and len(self.sort) > 0:
|
|
71
|
+
sort_options = (str(sort_item) for sort_item in self.sort)
|
|
72
|
+
ordered_sort_options = sorted(sort_options)
|
|
73
|
+
params["sort"] = ",".join(ordered_sort_options)
|
|
74
|
+
if len(self.filter) > 0:
|
|
75
|
+
filter_options = (str(filter_item) for filter_item in self.filter)
|
|
76
|
+
ordered_filter_options = sorted(filter_options)
|
|
77
|
+
params["filter"] = ",".join(ordered_filter_options)
|
|
78
|
+
if self._all_fields:
|
|
79
|
+
params["fields"] = "_all_"
|
|
80
|
+
if self.pagenumber:
|
|
81
|
+
params["pageNumber"] = self.pagenumber
|
|
82
|
+
if self.pagesize:
|
|
83
|
+
params["pageSize"] = self.pagesize
|
|
84
|
+
return params
|
|
85
|
+
|
|
86
|
+
def page_size(self, page_size):
|
|
87
|
+
self.pagesize = page_size
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
def page_number(self, page_number):
|
|
91
|
+
self.pagenumber = page_number
|
|
92
|
+
return self
|
|
93
|
+
|
|
34
94
|
class Operator:
|
|
35
95
|
Equals = "eq"
|
|
36
96
|
GreaterThan = "gt"
|
|
@@ -41,6 +101,7 @@ class RequestOptions(RequestOptionsBase):
|
|
|
41
101
|
Has = "has"
|
|
42
102
|
CaseInsensitiveEquals = "cieq"
|
|
43
103
|
|
|
104
|
+
# These are fields in the REST API
|
|
44
105
|
class Field:
|
|
45
106
|
Args = "args"
|
|
46
107
|
AuthenticationType = "authenticationType"
|
|
@@ -83,6 +144,7 @@ class RequestOptions(RequestOptionsBase):
|
|
|
83
144
|
NotificationType = "notificationType"
|
|
84
145
|
OwnerDomain = "ownerDomain"
|
|
85
146
|
OwnerEmail = "ownerEmail"
|
|
147
|
+
OwnerId = "ownerId"
|
|
86
148
|
OwnerName = "ownerName"
|
|
87
149
|
ParentProjectId = "parentProjectId"
|
|
88
150
|
Priority = "priority"
|
|
@@ -109,68 +171,93 @@ class RequestOptions(RequestOptionsBase):
|
|
|
109
171
|
UpdatedAt = "updatedAt"
|
|
110
172
|
UserCount = "userCount"
|
|
111
173
|
UserId = "userId"
|
|
174
|
+
ViewId = "viewId"
|
|
112
175
|
ViewUrlName = "viewUrlName"
|
|
113
176
|
WorkbookDescription = "workbookDescription"
|
|
177
|
+
WorkbookId = "workbookId"
|
|
114
178
|
WorkbookName = "workbookName"
|
|
115
179
|
|
|
116
180
|
class Direction:
|
|
117
181
|
Desc = "desc"
|
|
118
182
|
Asc = "asc"
|
|
119
183
|
|
|
120
|
-
def __init__(self, pagenumber=1, pagesize=None):
|
|
121
|
-
self.pagenumber = pagenumber
|
|
122
|
-
self.pagesize = pagesize or config.PAGE_SIZE
|
|
123
|
-
self.sort = set()
|
|
124
|
-
self.filter = set()
|
|
125
184
|
|
|
126
|
-
|
|
127
|
-
|
|
185
|
+
"""
|
|
186
|
+
These options can be used by methods that are fetching data exported from a specific content item
|
|
187
|
+
"""
|
|
128
188
|
|
|
129
|
-
def page_size(self, page_size):
|
|
130
|
-
self.pagesize = page_size
|
|
131
|
-
return self
|
|
132
189
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
190
|
+
class _DataExportOptions(RequestOptionsBase):
|
|
191
|
+
def __init__(self, maxage: int = -1):
|
|
192
|
+
super().__init__()
|
|
193
|
+
self.view_filters: list[tuple[str, str]] = []
|
|
194
|
+
self.view_parameters: list[tuple[str, str]] = []
|
|
195
|
+
self.max_age: Optional[int] = maxage
|
|
196
|
+
"""
|
|
197
|
+
This setting will affect the contents of the workbook as they are exported.
|
|
198
|
+
Valid language values are tableau-supported languages like de, es, en
|
|
199
|
+
If no locale is specified, the default locale for that language will be used
|
|
200
|
+
"""
|
|
201
|
+
self.language: Optional[str] = None
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def max_age(self) -> int:
|
|
205
|
+
return self._max_age
|
|
206
|
+
|
|
207
|
+
@max_age.setter
|
|
208
|
+
@property_is_int(range=(0, 240), allowed=[-1])
|
|
209
|
+
def max_age(self, value):
|
|
210
|
+
self._max_age = value
|
|
136
211
|
|
|
137
212
|
def get_query_params(self):
|
|
138
213
|
params = {}
|
|
139
|
-
if self.
|
|
140
|
-
params["
|
|
141
|
-
if self.
|
|
142
|
-
params["
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
ordered_sort_options = sorted(sort_options)
|
|
146
|
-
params["sort"] = ",".join(ordered_sort_options)
|
|
147
|
-
if len(self.filter) > 0:
|
|
148
|
-
filter_options = (str(filter_item) for filter_item in self.filter)
|
|
149
|
-
ordered_filter_options = sorted(filter_options)
|
|
150
|
-
params["filter"] = ",".join(ordered_filter_options)
|
|
151
|
-
if self._all_fields:
|
|
152
|
-
params["fields"] = "_all_"
|
|
214
|
+
if self.max_age != -1:
|
|
215
|
+
params["maxAge"] = self.max_age
|
|
216
|
+
if self.language:
|
|
217
|
+
params["language"] = self.language
|
|
218
|
+
|
|
219
|
+
self._append_view_filters(params)
|
|
153
220
|
return params
|
|
154
221
|
|
|
222
|
+
def vf(self, name: str, value: str) -> Self:
|
|
223
|
+
"""Apply a filter based on a column within the view.
|
|
224
|
+
Note that when filtering on a boolean type field, the only valid values are 'true' and 'false'
|
|
155
225
|
|
|
156
|
-
|
|
157
|
-
"""Provide a basic implementation of adding view filters to the url"""
|
|
226
|
+
For more detail see: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_filtering_and_sorting.htm#Filter-query-views
|
|
158
227
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
228
|
+
Parameters
|
|
229
|
+
----------
|
|
230
|
+
name: str
|
|
231
|
+
The name of the column to filter on
|
|
162
232
|
|
|
163
|
-
|
|
164
|
-
|
|
233
|
+
value: str
|
|
234
|
+
The value to filter on
|
|
165
235
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
Self
|
|
239
|
+
The current object
|
|
240
|
+
"""
|
|
169
241
|
self.view_filters.append((name, value))
|
|
170
242
|
return self
|
|
171
243
|
|
|
172
244
|
def parameter(self, name: str, value: str) -> Self:
|
|
173
|
-
"""Apply a filter based on a parameter within the workbook.
|
|
245
|
+
"""Apply a filter based on a parameter within the workbook.
|
|
246
|
+
Note that when filtering on a boolean type field, the only valid values are 'true' and 'false'
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
name: str
|
|
251
|
+
The name of the parameter to filter on
|
|
252
|
+
|
|
253
|
+
value: str
|
|
254
|
+
The value to filter on
|
|
255
|
+
|
|
256
|
+
Returns
|
|
257
|
+
-------
|
|
258
|
+
Self
|
|
259
|
+
The current object
|
|
260
|
+
"""
|
|
174
261
|
self.view_parameters.append((name, value))
|
|
175
262
|
return self
|
|
176
263
|
|
|
@@ -181,82 +268,142 @@ class _FilterOptionsBase(RequestOptionsBase):
|
|
|
181
268
|
params[name] = value
|
|
182
269
|
|
|
183
270
|
|
|
184
|
-
class
|
|
185
|
-
def __init__(self, maxage=-1):
|
|
186
|
-
super(
|
|
187
|
-
self.
|
|
271
|
+
class _ImagePDFCommonExportOptions(_DataExportOptions):
|
|
272
|
+
def __init__(self, maxage=-1, viz_height=None, viz_width=None):
|
|
273
|
+
super().__init__(maxage=maxage)
|
|
274
|
+
self.viz_height = viz_height
|
|
275
|
+
self.viz_width = viz_width
|
|
188
276
|
|
|
189
277
|
@property
|
|
190
|
-
def
|
|
191
|
-
return self.
|
|
278
|
+
def viz_height(self):
|
|
279
|
+
return self._viz_height
|
|
192
280
|
|
|
193
|
-
@
|
|
194
|
-
@property_is_int(range=(0,
|
|
195
|
-
def
|
|
196
|
-
self.
|
|
281
|
+
@viz_height.setter
|
|
282
|
+
@property_is_int(range=(0, sys.maxsize), allowed=(None,))
|
|
283
|
+
def viz_height(self, value):
|
|
284
|
+
self._viz_height = value
|
|
197
285
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
286
|
+
@property
|
|
287
|
+
def viz_width(self):
|
|
288
|
+
return self._viz_width
|
|
289
|
+
|
|
290
|
+
@viz_width.setter
|
|
291
|
+
@property_is_int(range=(0, sys.maxsize), allowed=(None,))
|
|
292
|
+
def viz_width(self, value):
|
|
293
|
+
self._viz_width = value
|
|
294
|
+
|
|
295
|
+
def get_query_params(self) -> dict:
|
|
296
|
+
params = super().get_query_params()
|
|
297
|
+
|
|
298
|
+
# XOR. Either both are None or both are not None.
|
|
299
|
+
if (self.viz_height is None) ^ (self.viz_width is None):
|
|
300
|
+
raise ValueError("viz_height and viz_width must be specified together")
|
|
301
|
+
|
|
302
|
+
if self.viz_height is not None:
|
|
303
|
+
params["vizHeight"] = self.viz_height
|
|
304
|
+
|
|
305
|
+
if self.viz_width is not None:
|
|
306
|
+
params["vizWidth"] = self.viz_width
|
|
202
307
|
|
|
203
|
-
self._append_view_filters(params)
|
|
204
308
|
return params
|
|
205
309
|
|
|
206
310
|
|
|
207
|
-
class
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
311
|
+
class CSVRequestOptions(_DataExportOptions):
|
|
312
|
+
"""
|
|
313
|
+
Options that can be used when exporting a view to CSV. Set the maxage to control the age of the data exported.
|
|
314
|
+
Filters to the underlying data can be applied using the `vf` and `parameter` methods.
|
|
211
315
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
316
|
+
Parameters
|
|
317
|
+
----------
|
|
318
|
+
maxage: int, optional
|
|
319
|
+
The maximum age of the data to export. Shortest possible duration is 1
|
|
320
|
+
minute. No upper limit. Default is -1, which means no limit.
|
|
321
|
+
"""
|
|
215
322
|
|
|
216
|
-
|
|
217
|
-
@property_is_int(range=(0, 240), allowed=[-1])
|
|
218
|
-
def max_age(self, value: int) -> None:
|
|
219
|
-
self._max_age = value
|
|
323
|
+
extension = "csv"
|
|
220
324
|
|
|
221
|
-
def get_query_params(self):
|
|
222
|
-
params = {}
|
|
223
|
-
if self.max_age != -1:
|
|
224
|
-
params["maxAge"] = self.max_age
|
|
225
325
|
|
|
226
|
-
|
|
227
|
-
|
|
326
|
+
class ExcelRequestOptions(_DataExportOptions):
|
|
327
|
+
"""
|
|
328
|
+
Options that can be used when exporting a view to Excel. Set the maxage to control the age of the data exported.
|
|
329
|
+
Filters to the underlying data can be applied using the `vf` and `parameter` methods.
|
|
330
|
+
|
|
331
|
+
Parameters
|
|
332
|
+
----------
|
|
333
|
+
maxage: int, optional
|
|
334
|
+
The maximum age of the data to export. Shortest possible duration is 1
|
|
335
|
+
minute. No upper limit. Default is -1, which means no limit.
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
extension = "xlsx"
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class ImageRequestOptions(_ImagePDFCommonExportOptions):
|
|
342
|
+
"""
|
|
343
|
+
Options that can be used when exporting a view to an image. Set the maxage to control the age of the data exported.
|
|
344
|
+
Filters to the underlying data can be applied using the `vf` and `parameter` methods.
|
|
345
|
+
|
|
346
|
+
Parameters
|
|
347
|
+
----------
|
|
348
|
+
imageresolution: str, optional
|
|
349
|
+
The resolution of the image to export. Valid values are "high" or None. Default is None.
|
|
350
|
+
Image width and actual pixel density are determined by the display context
|
|
351
|
+
of the image. Aspect ratio is always preserved. Set the value to "high" to
|
|
352
|
+
ensure maximum pixel density.
|
|
353
|
+
|
|
354
|
+
maxage: int, optional
|
|
355
|
+
The maximum age of the data to export. Shortest possible duration is 1
|
|
356
|
+
minute. No upper limit. Default is -1, which means no limit.
|
|
357
|
+
|
|
358
|
+
viz_height: int, optional
|
|
359
|
+
The height of the viz in pixels. If specified, viz_width must also be specified.
|
|
360
|
+
|
|
361
|
+
viz_width: int, optional
|
|
362
|
+
The width of the viz in pixels. If specified, viz_height must also be specified.
|
|
363
|
+
|
|
364
|
+
"""
|
|
228
365
|
|
|
366
|
+
extension = "png"
|
|
229
367
|
|
|
230
|
-
class ImageRequestOptions(_FilterOptionsBase):
|
|
231
368
|
# if 'high' isn't specified, the REST API endpoint returns an image with standard resolution
|
|
232
369
|
class Resolution:
|
|
233
370
|
High = "high"
|
|
234
371
|
|
|
235
|
-
def __init__(self, imageresolution=None, maxage=-1):
|
|
236
|
-
super(
|
|
372
|
+
def __init__(self, imageresolution=None, maxage=-1, viz_height=None, viz_width=None):
|
|
373
|
+
super().__init__(maxage=maxage, viz_height=viz_height, viz_width=viz_width)
|
|
237
374
|
self.image_resolution = imageresolution
|
|
238
|
-
self.max_age = maxage
|
|
239
|
-
|
|
240
|
-
@property
|
|
241
|
-
def max_age(self):
|
|
242
|
-
return self._max_age
|
|
243
|
-
|
|
244
|
-
@max_age.setter
|
|
245
|
-
@property_is_int(range=(0, 240), allowed=[-1])
|
|
246
|
-
def max_age(self, value):
|
|
247
|
-
self._max_age = value
|
|
248
375
|
|
|
249
376
|
def get_query_params(self):
|
|
250
|
-
params =
|
|
377
|
+
params = super().get_query_params()
|
|
251
378
|
if self.image_resolution:
|
|
252
379
|
params["resolution"] = self.image_resolution
|
|
253
|
-
if self.max_age != -1:
|
|
254
|
-
params["maxAge"] = self.max_age
|
|
255
|
-
self._append_view_filters(params)
|
|
256
380
|
return params
|
|
257
381
|
|
|
258
382
|
|
|
259
|
-
class PDFRequestOptions(
|
|
383
|
+
class PDFRequestOptions(_ImagePDFCommonExportOptions):
|
|
384
|
+
"""
|
|
385
|
+
Options that can be used when exporting a view to PDF. Set the maxage to control the age of the data exported.
|
|
386
|
+
Filters to the underlying data can be applied using the `vf` and `parameter` methods.
|
|
387
|
+
|
|
388
|
+
Parameters
|
|
389
|
+
----------
|
|
390
|
+
page_type: str, optional
|
|
391
|
+
The page type of the PDF to export. Valid values are accessible via the `PageType` class.
|
|
392
|
+
|
|
393
|
+
orientation: str, optional
|
|
394
|
+
The orientation of the PDF to export. Valid values are accessible via the `Orientation` class.
|
|
395
|
+
|
|
396
|
+
maxage: int, optional
|
|
397
|
+
The maximum age of the data to export. Shortest possible duration is 1
|
|
398
|
+
minute. No upper limit. Default is -1, which means no limit.
|
|
399
|
+
|
|
400
|
+
viz_height: int, optional
|
|
401
|
+
The height of the viz in pixels. If specified, viz_width must also be specified.
|
|
402
|
+
|
|
403
|
+
viz_width: int, optional
|
|
404
|
+
The width of the viz in pixels. If specified, viz_height must also be specified.
|
|
405
|
+
"""
|
|
406
|
+
|
|
260
407
|
class PageType:
|
|
261
408
|
A3 = "a3"
|
|
262
409
|
A4 = "a4"
|
|
@@ -278,61 +425,16 @@ class PDFRequestOptions(_FilterOptionsBase):
|
|
|
278
425
|
Landscape = "landscape"
|
|
279
426
|
|
|
280
427
|
def __init__(self, page_type=None, orientation=None, maxage=-1, viz_height=None, viz_width=None):
|
|
281
|
-
super(
|
|
428
|
+
super().__init__(maxage=maxage, viz_height=viz_height, viz_width=viz_width)
|
|
282
429
|
self.page_type = page_type
|
|
283
430
|
self.orientation = orientation
|
|
284
|
-
self.max_age = maxage
|
|
285
|
-
self.viz_height = viz_height
|
|
286
|
-
self.viz_width = viz_width
|
|
287
|
-
|
|
288
|
-
@property
|
|
289
|
-
def max_age(self):
|
|
290
|
-
return self._max_age
|
|
291
|
-
|
|
292
|
-
@max_age.setter
|
|
293
|
-
@property_is_int(range=(0, 240), allowed=[-1])
|
|
294
|
-
def max_age(self, value):
|
|
295
|
-
self._max_age = value
|
|
296
|
-
|
|
297
|
-
@property
|
|
298
|
-
def viz_height(self):
|
|
299
|
-
return self._viz_height
|
|
300
431
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
def viz_height(self, value):
|
|
304
|
-
self._viz_height = value
|
|
305
|
-
|
|
306
|
-
@property
|
|
307
|
-
def viz_width(self):
|
|
308
|
-
return self._viz_width
|
|
309
|
-
|
|
310
|
-
@viz_width.setter
|
|
311
|
-
@property_is_int(range=(0, sys.maxsize), allowed=(None,))
|
|
312
|
-
def viz_width(self, value):
|
|
313
|
-
self._viz_width = value
|
|
314
|
-
|
|
315
|
-
def get_query_params(self):
|
|
316
|
-
params = {}
|
|
432
|
+
def get_query_params(self) -> dict:
|
|
433
|
+
params = super().get_query_params()
|
|
317
434
|
if self.page_type:
|
|
318
435
|
params["type"] = self.page_type
|
|
319
436
|
|
|
320
437
|
if self.orientation:
|
|
321
438
|
params["orientation"] = self.orientation
|
|
322
439
|
|
|
323
|
-
if self.max_age != -1:
|
|
324
|
-
params["maxAge"] = self.max_age
|
|
325
|
-
|
|
326
|
-
# XOR. Either both are None or both are not None.
|
|
327
|
-
if (self.viz_height is None) ^ (self.viz_width is None):
|
|
328
|
-
raise ValueError("viz_height and viz_width must be specified together")
|
|
329
|
-
|
|
330
|
-
if self.viz_height is not None:
|
|
331
|
-
params["vizHeight"] = self.viz_height
|
|
332
|
-
|
|
333
|
-
if self.viz_width is not None:
|
|
334
|
-
params["vizWidth"] = self.viz_width
|
|
335
|
-
|
|
336
|
-
self._append_view_filters(params)
|
|
337
|
-
|
|
338
440
|
return params
|
|
@@ -58,11 +58,68 @@ minimum_supported_server_version = "2.3"
|
|
|
58
58
|
default_server_version = "2.4" # first version that dropped the legacy auth endpoint
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
class Server
|
|
61
|
+
class Server:
|
|
62
|
+
"""
|
|
63
|
+
In the Tableau REST API, the server (https://MY-SERVER/) is the base or core
|
|
64
|
+
of the URI that makes up the various endpoints or methods for accessing
|
|
65
|
+
resources on the server (views, workbooks, sites, users, data sources, etc.)
|
|
66
|
+
The TSC library provides a Server class that represents the server. You
|
|
67
|
+
create a server instance to sign in to the server and to call the various
|
|
68
|
+
methods for accessing resources.
|
|
69
|
+
|
|
70
|
+
The Server class contains the attributes that represent the server on
|
|
71
|
+
Tableau Server. After you create an instance of the Server class, you can
|
|
72
|
+
sign in to the server and call methods to access all of the resources on the
|
|
73
|
+
server.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
server_address : str
|
|
78
|
+
Specifies the address of the Tableau Server or Tableau Cloud (for
|
|
79
|
+
example, https://MY-SERVER/).
|
|
80
|
+
|
|
81
|
+
use_server_version : bool
|
|
82
|
+
Specifies the version of the REST API to use (for example, '2.5'). When
|
|
83
|
+
you use the TSC library to call methods that access Tableau Server, the
|
|
84
|
+
version is passed to the endpoint as part of the URI
|
|
85
|
+
(https://MY-SERVER/api/2.5/). Each release of Tableau Server supports
|
|
86
|
+
specific versions of the REST API. New versions of the REST API are
|
|
87
|
+
released with Tableau Server. By default, the value of version is set to
|
|
88
|
+
'2.3', which corresponds to Tableau Server 10.0. You can view or set
|
|
89
|
+
this value. You might need to set this to a different value, for
|
|
90
|
+
example, if you want to access features that are supported by the server
|
|
91
|
+
and a later version of the REST API. For more information, see REST API
|
|
92
|
+
Versions.
|
|
93
|
+
|
|
94
|
+
Examples
|
|
95
|
+
--------
|
|
96
|
+
>>> import tableauserverclient as TSC
|
|
97
|
+
|
|
98
|
+
>>> # create a instance of server
|
|
99
|
+
>>> server = TSC.Server('https://MY-SERVER')
|
|
100
|
+
|
|
101
|
+
>>> # sign in, etc.
|
|
102
|
+
|
|
103
|
+
>>> # change the REST API version to match the server
|
|
104
|
+
>>> server.use_server_version()
|
|
105
|
+
|
|
106
|
+
>>> # or change the REST API version to match a specific version
|
|
107
|
+
>>> # for example, 2.8
|
|
108
|
+
>>> # server.version = '2.8'
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
|
|
62
112
|
class PublishMode:
|
|
113
|
+
"""
|
|
114
|
+
Enumerates the options that specify what happens when you publish a
|
|
115
|
+
workbook or data source. The options are Overwrite, Append, or
|
|
116
|
+
CreateNew.
|
|
117
|
+
"""
|
|
118
|
+
|
|
63
119
|
Append = "Append"
|
|
64
120
|
Overwrite = "Overwrite"
|
|
65
121
|
CreateNew = "CreateNew"
|
|
122
|
+
Replace = "Replace"
|
|
66
123
|
|
|
67
124
|
def __init__(self, server_address, use_server_version=False, http_options=None, session_factory=None):
|
|
68
125
|
self._auth_token = None
|
|
@@ -130,7 +187,7 @@ class Server(object):
|
|
|
130
187
|
raise ValueError("Server connection settings not valid", req_ex)
|
|
131
188
|
|
|
132
189
|
def __repr__(self):
|
|
133
|
-
return "<TableauServerClient [Connection: {
|
|
190
|
+
return f"<TableauServerClient [Connection: {self.baseurl}, {self.server_info.serverInfo}]>"
|
|
134
191
|
|
|
135
192
|
def add_http_options(self, options_dict: dict):
|
|
136
193
|
try:
|
|
@@ -142,7 +199,7 @@ class Server(object):
|
|
|
142
199
|
# expected errors on invalid input:
|
|
143
200
|
# 'set' object has no attribute 'keys', 'list' object has no attribute 'keys'
|
|
144
201
|
# TypeError: cannot convert dictionary update sequence element #0 to a sequence (input is a tuple)
|
|
145
|
-
raise ValueError("Invalid http options given: {}"
|
|
202
|
+
raise ValueError(f"Invalid http options given: {options_dict}")
|
|
146
203
|
|
|
147
204
|
def clear_http_options(self):
|
|
148
205
|
self._http_options = dict()
|
|
@@ -151,12 +208,14 @@ class Server(object):
|
|
|
151
208
|
self._site_id = None
|
|
152
209
|
self._user_id = None
|
|
153
210
|
self._auth_token = None
|
|
211
|
+
self._site_url = None
|
|
154
212
|
self._session = self._session_factory()
|
|
155
213
|
|
|
156
|
-
def _set_auth(self, site_id, user_id, auth_token):
|
|
214
|
+
def _set_auth(self, site_id, user_id, auth_token, site_url=None):
|
|
157
215
|
self._site_id = site_id
|
|
158
216
|
self._user_id = user_id
|
|
159
217
|
self._auth_token = auth_token
|
|
218
|
+
self._site_url = site_url
|
|
160
219
|
|
|
161
220
|
def _get_legacy_version(self):
|
|
162
221
|
# the serverInfo call was introduced in 2.4, earlier than that we have this different call
|
|
@@ -176,15 +235,15 @@ class Server(object):
|
|
|
176
235
|
old_version = self.version
|
|
177
236
|
version = self.server_info.get().rest_api_version
|
|
178
237
|
except ServerInfoEndpointNotFoundError as e:
|
|
179
|
-
logger.info("Could not get version info from server: {}{}"
|
|
238
|
+
logger.info(f"Could not get version info from server: {e.__class__}{e}")
|
|
180
239
|
version = self._get_legacy_version()
|
|
181
240
|
except EndpointUnavailableError as e:
|
|
182
|
-
logger.info("Could not get version info from server: {}{}"
|
|
241
|
+
logger.info(f"Could not get version info from server: {e.__class__}{e}")
|
|
183
242
|
version = self._get_legacy_version()
|
|
184
243
|
except Exception as e:
|
|
185
|
-
logger.info("Could not get version info from server: {}{}"
|
|
244
|
+
logger.info(f"Could not get version info from server: {e.__class__}{e}")
|
|
186
245
|
version = None
|
|
187
|
-
logger.info("versions: {}, {}"
|
|
246
|
+
logger.info(f"versions: {version}, {old_version}")
|
|
188
247
|
return version or old_version
|
|
189
248
|
|
|
190
249
|
def use_server_version(self):
|
|
@@ -201,12 +260,12 @@ class Server(object):
|
|
|
201
260
|
|
|
202
261
|
def assert_at_least_version(self, comparison: str, reason: str):
|
|
203
262
|
if not self.check_at_least_version(comparison):
|
|
204
|
-
error = "{} is not available in API version {}. Requires {}"
|
|
263
|
+
error = f"{reason} is not available in API version {self.version}. Requires {comparison}"
|
|
205
264
|
raise EndpointUnavailableError(error)
|
|
206
265
|
|
|
207
266
|
@property
|
|
208
267
|
def baseurl(self):
|
|
209
|
-
return "{
|
|
268
|
+
return f"{self._server_address}/api/{str(self.version)}"
|
|
210
269
|
|
|
211
270
|
@property
|
|
212
271
|
def namespace(self):
|
|
@@ -226,6 +285,13 @@ class Server(object):
|
|
|
226
285
|
raise NotSignedInError(error)
|
|
227
286
|
return self._site_id
|
|
228
287
|
|
|
288
|
+
@property
|
|
289
|
+
def site_url(self):
|
|
290
|
+
if self._site_url is None:
|
|
291
|
+
error = "Missing site URL. You must sign in first."
|
|
292
|
+
raise NotSignedInError(error)
|
|
293
|
+
return self._site_url
|
|
294
|
+
|
|
229
295
|
@property
|
|
230
296
|
def user_id(self):
|
|
231
297
|
if self._user_id is None:
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
class Sort
|
|
1
|
+
class Sort:
|
|
2
|
+
"""
|
|
3
|
+
Used with request options (RequestOptions) where you can filter and sort on
|
|
4
|
+
the results returned from the server.
|
|
5
|
+
|
|
6
|
+
Parameters
|
|
7
|
+
----------
|
|
8
|
+
field : str
|
|
9
|
+
Sets the field to sort on. The fields are defined in the RequestOption class.
|
|
10
|
+
|
|
11
|
+
direction : str
|
|
12
|
+
The direction to sort, either ascending (Asc) or descending (Desc). The
|
|
13
|
+
options are defined in the RequestOptions.Direction class.
|
|
14
|
+
"""
|
|
15
|
+
|
|
2
16
|
def __init__(self, field, direction):
|
|
3
17
|
self.field = field
|
|
4
18
|
self.direction = direction
|
|
5
19
|
|
|
6
20
|
def __str__(self):
|
|
7
|
-
return "{
|
|
21
|
+
return f"{self.field}:{self.direction}"
|