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
|
@@ -7,6 +7,7 @@ from contextlib import closing
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
from tableauserverclient.helpers.headers import fix_filename
|
|
10
|
+
from tableauserverclient.models.permissions_item import PermissionsRule
|
|
10
11
|
from tableauserverclient.server.query import QuerySet
|
|
11
12
|
|
|
12
13
|
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api, parameter_added_in
|
|
@@ -25,15 +26,11 @@ from tableauserverclient.models import WorkbookItem, ConnectionItem, ViewItem, P
|
|
|
25
26
|
from tableauserverclient.server import RequestFactory
|
|
26
27
|
|
|
27
28
|
from typing import (
|
|
28
|
-
Iterable,
|
|
29
|
-
List,
|
|
30
29
|
Optional,
|
|
31
|
-
Sequence,
|
|
32
|
-
Set,
|
|
33
|
-
Tuple,
|
|
34
30
|
TYPE_CHECKING,
|
|
35
31
|
Union,
|
|
36
32
|
)
|
|
33
|
+
from collections.abc import Iterable, Sequence
|
|
37
34
|
|
|
38
35
|
if TYPE_CHECKING:
|
|
39
36
|
from tableauserverclient.server import Server
|
|
@@ -61,18 +58,34 @@ PathOrFileW = Union[FilePath, FileObjectW]
|
|
|
61
58
|
|
|
62
59
|
class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
63
60
|
def __init__(self, parent_srv: "Server") -> None:
|
|
64
|
-
super(
|
|
61
|
+
super().__init__(parent_srv)
|
|
65
62
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
66
63
|
|
|
67
64
|
return None
|
|
68
65
|
|
|
69
66
|
@property
|
|
70
67
|
def baseurl(self) -> str:
|
|
71
|
-
return "{
|
|
68
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/workbooks"
|
|
72
69
|
|
|
73
70
|
# Get all workbooks on site
|
|
74
71
|
@api(version="2.0")
|
|
75
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
72
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[WorkbookItem], PaginationItem]:
|
|
73
|
+
"""
|
|
74
|
+
Queries the server and returns information about the workbooks the site.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
req_options : RequestOptions, optional
|
|
79
|
+
(Optional) You can pass the method a request object that contains
|
|
80
|
+
additional parameters to filter the request. For example, if you
|
|
81
|
+
were searching for a specific workbook, you could specify the name
|
|
82
|
+
of the workbook or the name of the owner.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
Tuple containing one page's worth of workbook items and pagination
|
|
87
|
+
information.
|
|
88
|
+
"""
|
|
76
89
|
logger.info("Querying all workbooks on site")
|
|
77
90
|
url = self.baseurl
|
|
78
91
|
server_response = self.get_request(url, req_options)
|
|
@@ -83,20 +96,48 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
83
96
|
# Get 1 workbook
|
|
84
97
|
@api(version="2.0")
|
|
85
98
|
def get_by_id(self, workbook_id: str) -> WorkbookItem:
|
|
99
|
+
"""
|
|
100
|
+
Returns information about the specified workbook on the site.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
workbook_id : str
|
|
105
|
+
The workbook ID.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
WorkbookItem
|
|
110
|
+
The workbook item.
|
|
111
|
+
"""
|
|
86
112
|
if not workbook_id:
|
|
87
113
|
error = "Workbook ID undefined."
|
|
88
114
|
raise ValueError(error)
|
|
89
|
-
logger.info("Querying single workbook (ID: {
|
|
90
|
-
url = "{
|
|
115
|
+
logger.info(f"Querying single workbook (ID: {workbook_id})")
|
|
116
|
+
url = f"{self.baseurl}/{workbook_id}"
|
|
91
117
|
server_response = self.get_request(url)
|
|
92
118
|
return WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
93
119
|
|
|
94
120
|
@api(version="2.8")
|
|
95
|
-
def refresh(self, workbook_item: Union[WorkbookItem, str]) -> JobItem:
|
|
121
|
+
def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = False) -> JobItem:
|
|
122
|
+
"""
|
|
123
|
+
Refreshes the extract of an existing workbook.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
workbook_item : WorkbookItem | str
|
|
128
|
+
The workbook item or workbook ID.
|
|
129
|
+
incremental: bool
|
|
130
|
+
Whether to do a full refresh or incremental refresh of the extract data
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
JobItem
|
|
135
|
+
The job item.
|
|
136
|
+
"""
|
|
96
137
|
id_ = getattr(workbook_item, "id", workbook_item)
|
|
97
|
-
url = "{
|
|
98
|
-
|
|
99
|
-
server_response = self.post_request(url,
|
|
138
|
+
url = f"{self.baseurl}/{id_}/refresh"
|
|
139
|
+
refresh_req = RequestFactory.Task.refresh_req(incremental)
|
|
140
|
+
server_response = self.post_request(url, refresh_req)
|
|
100
141
|
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
101
142
|
return new_job
|
|
102
143
|
|
|
@@ -107,10 +148,37 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
107
148
|
workbook_item: WorkbookItem,
|
|
108
149
|
encrypt: bool = False,
|
|
109
150
|
includeAll: bool = True,
|
|
110
|
-
datasources: Optional[
|
|
151
|
+
datasources: Optional[list["DatasourceItem"]] = None,
|
|
111
152
|
) -> JobItem:
|
|
153
|
+
"""
|
|
154
|
+
Create one or more extracts on 1 workbook, optionally encrypted.
|
|
155
|
+
|
|
156
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#create_extracts_for_workbook
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
workbook_item : WorkbookItem
|
|
161
|
+
The workbook item to create extracts for.
|
|
162
|
+
|
|
163
|
+
encrypt : bool, default False
|
|
164
|
+
Set to True to encrypt the extracts.
|
|
165
|
+
|
|
166
|
+
includeAll : bool, default True
|
|
167
|
+
If True, all data sources in the workbook will have an extract
|
|
168
|
+
created for them. If False, then a data source must be supplied in
|
|
169
|
+
the request.
|
|
170
|
+
|
|
171
|
+
datasources : list[DatasourceItem] | None
|
|
172
|
+
List of DatasourceItem objects for the data sources to create
|
|
173
|
+
extracts for. Only required if includeAll is False.
|
|
174
|
+
|
|
175
|
+
Returns
|
|
176
|
+
-------
|
|
177
|
+
JobItem
|
|
178
|
+
The job item for the extract creation.
|
|
179
|
+
"""
|
|
112
180
|
id_ = getattr(workbook_item, "id", workbook_item)
|
|
113
|
-
url = "{
|
|
181
|
+
url = f"{self.baseurl}/{id_}/createExtract?encrypt={encrypt}"
|
|
114
182
|
|
|
115
183
|
datasource_req = RequestFactory.Workbook.embedded_extract_req(includeAll, datasources)
|
|
116
184
|
server_response = self.post_request(url, datasource_req)
|
|
@@ -120,8 +188,31 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
120
188
|
# delete all the extracts on 1 workbook
|
|
121
189
|
@api(version="3.3")
|
|
122
190
|
def delete_extract(self, workbook_item: WorkbookItem, includeAll: bool = True, datasources=None) -> JobItem:
|
|
191
|
+
"""
|
|
192
|
+
Delete all extracts of embedded datasources on 1 workbook.
|
|
193
|
+
|
|
194
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#delete_extracts_from_workbook
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
workbook_item : WorkbookItem
|
|
199
|
+
The workbook item to delete extracts from.
|
|
200
|
+
|
|
201
|
+
includeAll : bool, default True
|
|
202
|
+
If True, all data sources in the workbook will have their extracts
|
|
203
|
+
deleted. If False, then a data source must be supplied in the
|
|
204
|
+
request.
|
|
205
|
+
|
|
206
|
+
datasources : list[DatasourceItem] | None
|
|
207
|
+
List of DatasourceItem objects for the data sources to delete
|
|
208
|
+
extracts from. Only required if includeAll is False.
|
|
209
|
+
|
|
210
|
+
Returns
|
|
211
|
+
-------
|
|
212
|
+
JobItem
|
|
213
|
+
"""
|
|
123
214
|
id_ = getattr(workbook_item, "id", workbook_item)
|
|
124
|
-
url = "{
|
|
215
|
+
url = f"{self.baseurl}/{id_}/deleteExtract"
|
|
125
216
|
datasource_req = RequestFactory.Workbook.embedded_extract_req(includeAll, datasources)
|
|
126
217
|
server_response = self.post_request(url, datasource_req)
|
|
127
218
|
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
@@ -130,12 +221,24 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
130
221
|
# Delete 1 workbook by id
|
|
131
222
|
@api(version="2.0")
|
|
132
223
|
def delete(self, workbook_id: str) -> None:
|
|
224
|
+
"""
|
|
225
|
+
Deletes a workbook with the specified ID.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
workbook_id : str
|
|
230
|
+
The workbook ID.
|
|
231
|
+
|
|
232
|
+
Returns
|
|
233
|
+
-------
|
|
234
|
+
None
|
|
235
|
+
"""
|
|
133
236
|
if not workbook_id:
|
|
134
237
|
error = "Workbook ID undefined."
|
|
135
238
|
raise ValueError(error)
|
|
136
|
-
url = "{
|
|
239
|
+
url = f"{self.baseurl}/{workbook_id}"
|
|
137
240
|
self.delete_request(url)
|
|
138
|
-
logger.info("Deleted single workbook (ID: {
|
|
241
|
+
logger.info(f"Deleted single workbook (ID: {workbook_id})")
|
|
139
242
|
|
|
140
243
|
# Update workbook
|
|
141
244
|
@api(version="2.0")
|
|
@@ -145,6 +248,29 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
145
248
|
workbook_item: WorkbookItem,
|
|
146
249
|
include_view_acceleration_status: bool = False,
|
|
147
250
|
) -> WorkbookItem:
|
|
251
|
+
"""
|
|
252
|
+
Modifies an existing workbook. Use this method to change the owner or
|
|
253
|
+
the project that the workbook belongs to, or to change whether the
|
|
254
|
+
workbook shows views in tabs. The workbook item must include the
|
|
255
|
+
workbook ID and overrides the existing settings.
|
|
256
|
+
|
|
257
|
+
See https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#update_workbook
|
|
258
|
+
for a list of fields that can be updated.
|
|
259
|
+
|
|
260
|
+
Parameters
|
|
261
|
+
----------
|
|
262
|
+
workbook_item : WorkbookItem
|
|
263
|
+
The workbook item to update. ID is required. Other fields are
|
|
264
|
+
optional. Any fields that are not specified will not be changed.
|
|
265
|
+
|
|
266
|
+
include_view_acceleration_status : bool, default False
|
|
267
|
+
Set to True to include the view acceleration status in the response.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
WorkbookItem
|
|
272
|
+
The updated workbook item.
|
|
273
|
+
"""
|
|
148
274
|
if not workbook_item.id:
|
|
149
275
|
error = "Workbook item missing ID. Workbook must be retrieved from server first."
|
|
150
276
|
raise MissingRequiredFieldError(error)
|
|
@@ -152,27 +278,47 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
152
278
|
self.update_tags(workbook_item)
|
|
153
279
|
|
|
154
280
|
# Update the workbook itself
|
|
155
|
-
url = "{
|
|
281
|
+
url = f"{self.baseurl}/{workbook_item.id}"
|
|
156
282
|
if include_view_acceleration_status:
|
|
157
283
|
url += "?includeViewAccelerationStatus=True"
|
|
158
284
|
|
|
159
|
-
update_req = RequestFactory.Workbook.update_req(workbook_item)
|
|
285
|
+
update_req = RequestFactory.Workbook.update_req(workbook_item, self.parent_srv)
|
|
160
286
|
server_response = self.put_request(url, update_req)
|
|
161
|
-
logger.info("Updated workbook item (ID: {
|
|
287
|
+
logger.info(f"Updated workbook item (ID: {workbook_item.id})")
|
|
162
288
|
updated_workbook = copy.copy(workbook_item)
|
|
163
289
|
return updated_workbook._parse_common_tags(server_response.content, self.parent_srv.namespace)
|
|
164
290
|
|
|
165
291
|
# Update workbook_connection
|
|
166
292
|
@api(version="2.3")
|
|
167
293
|
def update_connection(self, workbook_item: WorkbookItem, connection_item: ConnectionItem) -> ConnectionItem:
|
|
168
|
-
|
|
294
|
+
"""
|
|
295
|
+
Updates a workbook connection information (server addres, server port,
|
|
296
|
+
user name, and password).
|
|
297
|
+
|
|
298
|
+
The workbook connections must be populated before the strings can be
|
|
299
|
+
updated.
|
|
300
|
+
|
|
301
|
+
Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#update_workbook_connection
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
workbook_item : WorkbookItem
|
|
306
|
+
The workbook item to update.
|
|
307
|
+
|
|
308
|
+
connection_item : ConnectionItem
|
|
309
|
+
The connection item to update.
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
ConnectionItem
|
|
314
|
+
The updated connection item.
|
|
315
|
+
"""
|
|
316
|
+
url = f"{self.baseurl}/{workbook_item.id}/connections/{connection_item.id}"
|
|
169
317
|
update_req = RequestFactory.Connection.update_req(connection_item)
|
|
170
318
|
server_response = self.put_request(url, update_req)
|
|
171
319
|
connection = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
172
320
|
|
|
173
|
-
logger.info(
|
|
174
|
-
"Updated workbook item (ID: {0} & connection item {1})".format(workbook_item.id, connection_item.id)
|
|
175
|
-
)
|
|
321
|
+
logger.info(f"Updated workbook item (ID: {workbook_item.id} & connection item {connection_item.id})")
|
|
176
322
|
return connection
|
|
177
323
|
|
|
178
324
|
# Download workbook contents with option of passing in filepath
|
|
@@ -185,6 +331,34 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
185
331
|
filepath: Optional[PathOrFileW] = None,
|
|
186
332
|
include_extract: bool = True,
|
|
187
333
|
) -> PathOrFileW:
|
|
334
|
+
"""
|
|
335
|
+
Downloads a workbook to the specified directory (optional).
|
|
336
|
+
|
|
337
|
+
Parameters
|
|
338
|
+
----------
|
|
339
|
+
workbook_id : str
|
|
340
|
+
The workbook ID.
|
|
341
|
+
|
|
342
|
+
filepath : Path or File object, optional
|
|
343
|
+
Downloads the file to the location you specify. If no location is
|
|
344
|
+
specified, the file is downloaded to the current working directory.
|
|
345
|
+
The default is Filepath=None.
|
|
346
|
+
|
|
347
|
+
include_extract : bool, default True
|
|
348
|
+
Set to False to exclude the extract from the download. The default
|
|
349
|
+
is True.
|
|
350
|
+
|
|
351
|
+
Returns
|
|
352
|
+
-------
|
|
353
|
+
Path or File object
|
|
354
|
+
The path to the downloaded workbook or the file object.
|
|
355
|
+
|
|
356
|
+
Raises
|
|
357
|
+
------
|
|
358
|
+
ValueError
|
|
359
|
+
If the workbook ID is not defined.
|
|
360
|
+
"""
|
|
361
|
+
|
|
188
362
|
return self.download_revision(
|
|
189
363
|
workbook_id,
|
|
190
364
|
None,
|
|
@@ -195,18 +369,48 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
195
369
|
# Get all views of workbook
|
|
196
370
|
@api(version="2.0")
|
|
197
371
|
def populate_views(self, workbook_item: WorkbookItem, usage: bool = False) -> None:
|
|
372
|
+
"""
|
|
373
|
+
Populates (or gets) a list of views for a workbook.
|
|
374
|
+
|
|
375
|
+
You must first call this method to populate views before you can iterate
|
|
376
|
+
through the views.
|
|
377
|
+
|
|
378
|
+
This method retrieves the view information for the specified workbook.
|
|
379
|
+
The REST API is designed to return only the information you ask for
|
|
380
|
+
explicitly. When you query for all the workbooks, the view information
|
|
381
|
+
is not included. Use this method to retrieve the views. The method adds
|
|
382
|
+
the list of views to the workbook item (workbook_item.views). This is a
|
|
383
|
+
list of ViewItem.
|
|
384
|
+
|
|
385
|
+
Parameters
|
|
386
|
+
----------
|
|
387
|
+
workbook_item : WorkbookItem
|
|
388
|
+
The workbook item to populate views for.
|
|
389
|
+
|
|
390
|
+
usage : bool, default False
|
|
391
|
+
Set to True to include usage statistics for each view.
|
|
392
|
+
|
|
393
|
+
Returns
|
|
394
|
+
-------
|
|
395
|
+
None
|
|
396
|
+
|
|
397
|
+
Raises
|
|
398
|
+
------
|
|
399
|
+
MissingRequiredFieldError
|
|
400
|
+
If the workbook item is missing an ID.
|
|
401
|
+
"""
|
|
198
402
|
if not workbook_item.id:
|
|
199
403
|
error = "Workbook item missing ID. Workbook must be retrieved from server first."
|
|
200
404
|
raise MissingRequiredFieldError(error)
|
|
201
405
|
|
|
202
|
-
def view_fetcher() ->
|
|
406
|
+
def view_fetcher() -> list[ViewItem]:
|
|
203
407
|
return self._get_views_for_workbook(workbook_item, usage)
|
|
204
408
|
|
|
205
409
|
workbook_item._set_views(view_fetcher)
|
|
206
|
-
logger.info("Populated views for workbook (ID: {
|
|
410
|
+
logger.info(f"Populated views for workbook (ID: {workbook_item.id})")
|
|
207
411
|
|
|
208
|
-
def _get_views_for_workbook(self, workbook_item: WorkbookItem, usage: bool) ->
|
|
209
|
-
url = "{
|
|
412
|
+
def _get_views_for_workbook(self, workbook_item: WorkbookItem, usage: bool) -> list[ViewItem]:
|
|
413
|
+
url = f"{self.baseurl}/{workbook_item.id}/views"
|
|
210
414
|
if usage:
|
|
211
415
|
url += "?includeUsageStatistics=true"
|
|
212
416
|
server_response = self.get_request(url)
|
|
@@ -220,6 +424,36 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
220
424
|
# Get all connections of workbook
|
|
221
425
|
@api(version="2.0")
|
|
222
426
|
def populate_connections(self, workbook_item: WorkbookItem) -> None:
|
|
427
|
+
"""
|
|
428
|
+
Populates a list of data source connections for the specified workbook.
|
|
429
|
+
|
|
430
|
+
You must populate connections before you can iterate through the
|
|
431
|
+
connections.
|
|
432
|
+
|
|
433
|
+
This method retrieves the data source connection information for the
|
|
434
|
+
specified workbook. The REST API is designed to return only the
|
|
435
|
+
information you ask for explicitly. When you query all the workbooks,
|
|
436
|
+
the data source connection information is not included. Use this method
|
|
437
|
+
to retrieve the connection information for any data sources used by the
|
|
438
|
+
workbook. The method adds the list of data connections to the workbook
|
|
439
|
+
item (workbook_item.connections). This is a list of ConnectionItem.
|
|
440
|
+
|
|
441
|
+
REST API docs: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#query_workbook_connections
|
|
442
|
+
|
|
443
|
+
Parameters
|
|
444
|
+
----------
|
|
445
|
+
workbook_item : WorkbookItem
|
|
446
|
+
The workbook item to populate connections for.
|
|
447
|
+
|
|
448
|
+
Returns
|
|
449
|
+
-------
|
|
450
|
+
None
|
|
451
|
+
|
|
452
|
+
Raises
|
|
453
|
+
------
|
|
454
|
+
MissingRequiredFieldError
|
|
455
|
+
If the workbook item is missing an ID.
|
|
456
|
+
"""
|
|
223
457
|
if not workbook_item.id:
|
|
224
458
|
error = "Workbook item missing ID. Workbook must be retrieved from server first."
|
|
225
459
|
raise MissingRequiredFieldError(error)
|
|
@@ -228,12 +462,12 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
228
462
|
return self._get_workbook_connections(workbook_item)
|
|
229
463
|
|
|
230
464
|
workbook_item._set_connections(connection_fetcher)
|
|
231
|
-
logger.info("Populated connections for workbook (ID: {
|
|
465
|
+
logger.info(f"Populated connections for workbook (ID: {workbook_item.id})")
|
|
232
466
|
|
|
233
467
|
def _get_workbook_connections(
|
|
234
468
|
self, workbook_item: WorkbookItem, req_options: Optional["RequestOptions"] = None
|
|
235
|
-
) ->
|
|
236
|
-
url = "{
|
|
469
|
+
) -> list[ConnectionItem]:
|
|
470
|
+
url = f"{self.baseurl}/{workbook_item.id}/connections"
|
|
237
471
|
server_response = self.get_request(url, req_options)
|
|
238
472
|
connections = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
239
473
|
return connections
|
|
@@ -241,6 +475,34 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
241
475
|
# Get the pdf of the entire workbook if its tabs are enabled, pdf of the default view if its tabs are disabled
|
|
242
476
|
@api(version="3.4")
|
|
243
477
|
def populate_pdf(self, workbook_item: WorkbookItem, req_options: Optional["RequestOptions"] = None) -> None:
|
|
478
|
+
"""
|
|
479
|
+
Populates the PDF for the specified workbook item.
|
|
480
|
+
|
|
481
|
+
This method populates a PDF with image(s) of the workbook view(s) you
|
|
482
|
+
specify.
|
|
483
|
+
|
|
484
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#download_workbook_pdf
|
|
485
|
+
|
|
486
|
+
Parameters
|
|
487
|
+
----------
|
|
488
|
+
workbook_item : WorkbookItem
|
|
489
|
+
The workbook item to populate the PDF for.
|
|
490
|
+
|
|
491
|
+
req_options : RequestOptions, optional
|
|
492
|
+
(Optional) You can pass in request options to specify the page type
|
|
493
|
+
and orientation of the PDF content, as well as the maximum age of
|
|
494
|
+
the PDF rendered on the server. See PDFRequestOptions class for more
|
|
495
|
+
details.
|
|
496
|
+
|
|
497
|
+
Returns
|
|
498
|
+
-------
|
|
499
|
+
None
|
|
500
|
+
|
|
501
|
+
Raises
|
|
502
|
+
------
|
|
503
|
+
MissingRequiredFieldError
|
|
504
|
+
If the workbook item is missing an ID.
|
|
505
|
+
"""
|
|
244
506
|
if not workbook_item.id:
|
|
245
507
|
error = "Workbook item missing ID."
|
|
246
508
|
raise MissingRequiredFieldError(error)
|
|
@@ -249,16 +511,46 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
249
511
|
return self._get_wb_pdf(workbook_item, req_options)
|
|
250
512
|
|
|
251
513
|
workbook_item._set_pdf(pdf_fetcher)
|
|
252
|
-
logger.info("Populated pdf for workbook (ID: {
|
|
514
|
+
logger.info(f"Populated pdf for workbook (ID: {workbook_item.id})")
|
|
253
515
|
|
|
254
516
|
def _get_wb_pdf(self, workbook_item: WorkbookItem, req_options: Optional["RequestOptions"]) -> bytes:
|
|
255
|
-
url = "{
|
|
517
|
+
url = f"{self.baseurl}/{workbook_item.id}/pdf"
|
|
256
518
|
server_response = self.get_request(url, req_options)
|
|
257
519
|
pdf = server_response.content
|
|
258
520
|
return pdf
|
|
259
521
|
|
|
260
522
|
@api(version="3.8")
|
|
261
523
|
def populate_powerpoint(self, workbook_item: WorkbookItem, req_options: Optional["RequestOptions"] = None) -> None:
|
|
524
|
+
"""
|
|
525
|
+
Populates the PowerPoint for the specified workbook item.
|
|
526
|
+
|
|
527
|
+
This method populates a PowerPoint with image(s) of the workbook view(s) you
|
|
528
|
+
specify.
|
|
529
|
+
|
|
530
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#download_workbook_powerpoint
|
|
531
|
+
|
|
532
|
+
Parameters
|
|
533
|
+
----------
|
|
534
|
+
workbook_item : WorkbookItem
|
|
535
|
+
The workbook item to populate the PDF for.
|
|
536
|
+
|
|
537
|
+
req_options : RequestOptions, optional
|
|
538
|
+
(Optional) You can pass in request options to specify the maximum
|
|
539
|
+
number of minutes a workbook .pptx will be cached before being
|
|
540
|
+
refreshed. To prevent multiple .pptx requests from overloading the
|
|
541
|
+
server, the shortest interval you can set is one minute. There is no
|
|
542
|
+
maximum value, but the server job enacting the caching action may
|
|
543
|
+
expire before a long cache period is reached.
|
|
544
|
+
|
|
545
|
+
Returns
|
|
546
|
+
-------
|
|
547
|
+
None
|
|
548
|
+
|
|
549
|
+
Raises
|
|
550
|
+
------
|
|
551
|
+
MissingRequiredFieldError
|
|
552
|
+
If the workbook item is missing an ID.
|
|
553
|
+
"""
|
|
262
554
|
if not workbook_item.id:
|
|
263
555
|
error = "Workbook item missing ID."
|
|
264
556
|
raise MissingRequiredFieldError(error)
|
|
@@ -267,10 +559,10 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
267
559
|
return self._get_wb_pptx(workbook_item, req_options)
|
|
268
560
|
|
|
269
561
|
workbook_item._set_powerpoint(pptx_fetcher)
|
|
270
|
-
logger.info("Populated powerpoint for workbook (ID: {
|
|
562
|
+
logger.info(f"Populated powerpoint for workbook (ID: {workbook_item.id})")
|
|
271
563
|
|
|
272
564
|
def _get_wb_pptx(self, workbook_item: WorkbookItem, req_options: Optional["RequestOptions"]) -> bytes:
|
|
273
|
-
url = "{
|
|
565
|
+
url = f"{self.baseurl}/{workbook_item.id}/powerpoint"
|
|
274
566
|
server_response = self.get_request(url, req_options)
|
|
275
567
|
pptx = server_response.content
|
|
276
568
|
return pptx
|
|
@@ -278,6 +570,26 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
278
570
|
# Get preview image of workbook
|
|
279
571
|
@api(version="2.0")
|
|
280
572
|
def populate_preview_image(self, workbook_item: WorkbookItem) -> None:
|
|
573
|
+
"""
|
|
574
|
+
This method gets the preview image (thumbnail) for the specified workbook item.
|
|
575
|
+
|
|
576
|
+
This method uses the workbook's ID to get the preview image. The method
|
|
577
|
+
adds the preview image to the workbook item (workbook_item.preview_image).
|
|
578
|
+
|
|
579
|
+
Parameters
|
|
580
|
+
----------
|
|
581
|
+
workbook_item : WorkbookItem
|
|
582
|
+
The workbook item to populate the preview image for.
|
|
583
|
+
|
|
584
|
+
Returns
|
|
585
|
+
-------
|
|
586
|
+
None
|
|
587
|
+
|
|
588
|
+
Raises
|
|
589
|
+
------
|
|
590
|
+
MissingRequiredFieldError
|
|
591
|
+
If the workbook item is missing an ID.
|
|
592
|
+
"""
|
|
281
593
|
if not workbook_item.id:
|
|
282
594
|
error = "Workbook item missing ID. Workbook must be retrieved from server first."
|
|
283
595
|
raise MissingRequiredFieldError(error)
|
|
@@ -286,24 +598,75 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
286
598
|
return self._get_wb_preview_image(workbook_item)
|
|
287
599
|
|
|
288
600
|
workbook_item._set_preview_image(image_fetcher)
|
|
289
|
-
logger.info("Populated preview image for workbook (ID: {
|
|
601
|
+
logger.info(f"Populated preview image for workbook (ID: {workbook_item.id})")
|
|
290
602
|
|
|
291
603
|
def _get_wb_preview_image(self, workbook_item: WorkbookItem) -> bytes:
|
|
292
|
-
url = "{
|
|
604
|
+
url = f"{self.baseurl}/{workbook_item.id}/previewImage"
|
|
293
605
|
server_response = self.get_request(url)
|
|
294
606
|
preview_image = server_response.content
|
|
295
607
|
return preview_image
|
|
296
608
|
|
|
297
609
|
@api(version="2.0")
|
|
298
610
|
def populate_permissions(self, item: WorkbookItem) -> None:
|
|
611
|
+
"""
|
|
612
|
+
Populates the permissions for the specified workbook item.
|
|
613
|
+
|
|
614
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_permissions.htm#query_workbook_permissions
|
|
615
|
+
|
|
616
|
+
Parameters
|
|
617
|
+
----------
|
|
618
|
+
item : WorkbookItem
|
|
619
|
+
The workbook item to populate permissions for.
|
|
620
|
+
|
|
621
|
+
Returns
|
|
622
|
+
-------
|
|
623
|
+
None
|
|
624
|
+
"""
|
|
299
625
|
self._permissions.populate(item)
|
|
300
626
|
|
|
301
627
|
@api(version="2.0")
|
|
302
|
-
def update_permissions(self, resource, rules):
|
|
628
|
+
def update_permissions(self, resource: WorkbookItem, rules: list[PermissionsRule]) -> list[PermissionsRule]:
|
|
629
|
+
"""
|
|
630
|
+
Updates the permissions for the specified workbook item. The method
|
|
631
|
+
replaces the existing permissions with the new permissions. Any missing
|
|
632
|
+
permissions are removed.
|
|
633
|
+
|
|
634
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_permissions.htm#replace_permissions_for_content
|
|
635
|
+
|
|
636
|
+
Parameters
|
|
637
|
+
----------
|
|
638
|
+
resource : WorkbookItem
|
|
639
|
+
The workbook item to update permissions for.
|
|
640
|
+
|
|
641
|
+
rules : list[PermissionsRule]
|
|
642
|
+
A list of permissions rules to apply to the workbook item.
|
|
643
|
+
|
|
644
|
+
Returns
|
|
645
|
+
-------
|
|
646
|
+
list[PermissionsRule]
|
|
647
|
+
The updated permissions rules.
|
|
648
|
+
"""
|
|
303
649
|
return self._permissions.update(resource, rules)
|
|
304
650
|
|
|
305
651
|
@api(version="2.0")
|
|
306
|
-
def delete_permission(self, item, capability_item):
|
|
652
|
+
def delete_permission(self, item: WorkbookItem, capability_item: PermissionsRule) -> None:
|
|
653
|
+
"""
|
|
654
|
+
Deletes a single permission rule from the specified workbook item.
|
|
655
|
+
|
|
656
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_permissions.htm#delete_workbook_permission
|
|
657
|
+
|
|
658
|
+
Parameters
|
|
659
|
+
----------
|
|
660
|
+
item : WorkbookItem
|
|
661
|
+
The workbook item to delete the permission from.
|
|
662
|
+
|
|
663
|
+
capability_item : PermissionsRule
|
|
664
|
+
The permission rule to delete.
|
|
665
|
+
|
|
666
|
+
Returns
|
|
667
|
+
-------
|
|
668
|
+
None
|
|
669
|
+
"""
|
|
307
670
|
return self._permissions.delete(item, capability_item)
|
|
308
671
|
|
|
309
672
|
@api(version="2.0")
|
|
@@ -319,10 +682,87 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
319
682
|
skip_connection_check: bool = False,
|
|
320
683
|
parameters=None,
|
|
321
684
|
):
|
|
685
|
+
"""
|
|
686
|
+
Publish a workbook to the specified site.
|
|
687
|
+
|
|
688
|
+
Note: The REST API cannot automatically include extracts or other
|
|
689
|
+
resources that the workbook uses. Therefore, a .twb file that uses data
|
|
690
|
+
from an Excel or csv file on a local computer cannot be published,
|
|
691
|
+
unless you package the data and workbook in a .twbx file, or publish the
|
|
692
|
+
data source separately.
|
|
693
|
+
|
|
694
|
+
For workbooks that are larger than 64 MB, the publish method
|
|
695
|
+
automatically takes care of chunking the file in parts for uploading.
|
|
696
|
+
Using this method is considerably more convenient than calling the
|
|
697
|
+
publish REST APIs directly.
|
|
698
|
+
|
|
699
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#publish_workbook
|
|
700
|
+
|
|
701
|
+
Parameters
|
|
702
|
+
----------
|
|
703
|
+
workbook_item : WorkbookItem
|
|
704
|
+
The workbook_item specifies the workbook you are publishing. When
|
|
705
|
+
you are adding a workbook, you need to first create a new instance
|
|
706
|
+
of a workbook_item that includes a project_id of an existing
|
|
707
|
+
project. The name of the workbook will be the name of the file,
|
|
708
|
+
unless you also specify a name for the new workbook when you create
|
|
709
|
+
the instance.
|
|
710
|
+
|
|
711
|
+
file : Path or File object
|
|
712
|
+
The file path or file object of the workbook to publish. When
|
|
713
|
+
providing a file object, you must also specifiy the name of the
|
|
714
|
+
workbook in your instance of the workbook_itemworkbook_item , as
|
|
715
|
+
the name cannot be derived from the file name.
|
|
716
|
+
|
|
717
|
+
mode : str
|
|
718
|
+
Specifies whether you are publishing a new workbook (CreateNew) or
|
|
719
|
+
overwriting an existing workbook (Overwrite). You cannot appending
|
|
720
|
+
workbooks. You can also use the publish mode attributes, for
|
|
721
|
+
example: TSC.Server.PublishMode.Overwrite.
|
|
722
|
+
|
|
723
|
+
connections : list[ConnectionItem] | None
|
|
724
|
+
List of ConnectionItems objects for the connections created within
|
|
725
|
+
the workbook.
|
|
726
|
+
|
|
727
|
+
as_job : bool, default False
|
|
728
|
+
Set to True to run the upload as a job (asynchronous upload). If set
|
|
729
|
+
to True a job will start to perform the publishing process and a Job
|
|
730
|
+
object is returned. Defaults to False.
|
|
731
|
+
|
|
732
|
+
skip_connection_check : bool, default False
|
|
733
|
+
Set to True to skip connection check at time of upload. Publishing
|
|
734
|
+
will succeed but unchecked connection issues may result in a
|
|
735
|
+
non-functioning workbook. Defaults to False.
|
|
736
|
+
|
|
737
|
+
Raises
|
|
738
|
+
------
|
|
739
|
+
OSError
|
|
740
|
+
If the file path does not lead to an existing file.
|
|
741
|
+
|
|
742
|
+
ServerResponseError
|
|
743
|
+
If the server response is not successful.
|
|
744
|
+
|
|
745
|
+
TypeError
|
|
746
|
+
If the file is not a file path or file object.
|
|
747
|
+
|
|
748
|
+
ValueError
|
|
749
|
+
If the file extension is not supported
|
|
750
|
+
|
|
751
|
+
ValueError
|
|
752
|
+
If the mode is invalid.
|
|
753
|
+
|
|
754
|
+
ValueError
|
|
755
|
+
Workbooks cannot be appended.
|
|
756
|
+
|
|
757
|
+
Returns
|
|
758
|
+
-------
|
|
759
|
+
WorkbookItem | JobItem
|
|
760
|
+
The workbook item or job item that was published.
|
|
761
|
+
"""
|
|
322
762
|
if isinstance(file, (str, os.PathLike)):
|
|
323
763
|
if not os.path.isfile(file):
|
|
324
764
|
error = "File path does not lead to an existing file."
|
|
325
|
-
raise
|
|
765
|
+
raise OSError(error)
|
|
326
766
|
|
|
327
767
|
filename = os.path.basename(file)
|
|
328
768
|
file_extension = os.path.splitext(filename)[1][1:]
|
|
@@ -346,12 +786,12 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
346
786
|
elif file_type == "xml":
|
|
347
787
|
file_extension = "twb"
|
|
348
788
|
else:
|
|
349
|
-
error = "Unsupported file type {}!"
|
|
789
|
+
error = f"Unsupported file type {file_type}!"
|
|
350
790
|
raise ValueError(error)
|
|
351
791
|
|
|
352
792
|
# Generate filename for file object.
|
|
353
793
|
# This is needed when publishing the workbook in a single request
|
|
354
|
-
filename = "{}.{}"
|
|
794
|
+
filename = f"{workbook_item.name}.{file_extension}"
|
|
355
795
|
file_size = get_file_object_size(file)
|
|
356
796
|
|
|
357
797
|
else:
|
|
@@ -362,30 +802,30 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
362
802
|
raise ValueError(error)
|
|
363
803
|
|
|
364
804
|
# Construct the url with the defined mode
|
|
365
|
-
url = "{
|
|
805
|
+
url = f"{self.baseurl}?workbookType={file_extension}"
|
|
366
806
|
if mode == self.parent_srv.PublishMode.Overwrite:
|
|
367
|
-
url += "&{
|
|
807
|
+
url += f"&{mode.lower()}=true"
|
|
368
808
|
elif mode == self.parent_srv.PublishMode.Append:
|
|
369
809
|
error = "Workbooks cannot be appended."
|
|
370
810
|
raise ValueError(error)
|
|
371
811
|
|
|
372
812
|
if as_job:
|
|
373
|
-
url += "&{
|
|
813
|
+
url += "&{}=true".format("asJob")
|
|
374
814
|
|
|
375
815
|
if skip_connection_check:
|
|
376
|
-
url += "&{
|
|
816
|
+
url += "&{}=true".format("skipConnectionCheck")
|
|
377
817
|
|
|
378
818
|
# Determine if chunking is required (64MB is the limit for single upload method)
|
|
379
819
|
if file_size >= FILESIZE_LIMIT:
|
|
380
|
-
logger.info("Publishing {
|
|
820
|
+
logger.info(f"Publishing {workbook_item.name} to server with chunking method (workbook over 64MB)")
|
|
381
821
|
upload_session_id = self.parent_srv.fileuploads.upload(file)
|
|
382
|
-
url = "{
|
|
822
|
+
url = f"{url}&uploadSessionId={upload_session_id}"
|
|
383
823
|
xml_request, content_type = RequestFactory.Workbook.publish_req_chunked(
|
|
384
824
|
workbook_item,
|
|
385
825
|
connections=connections,
|
|
386
826
|
)
|
|
387
827
|
else:
|
|
388
|
-
logger.info("Publishing {
|
|
828
|
+
logger.info(f"Publishing {filename} to server")
|
|
389
829
|
|
|
390
830
|
if isinstance(file, (str, Path)):
|
|
391
831
|
with open(file, "rb") as f:
|
|
@@ -403,7 +843,7 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
403
843
|
file_contents,
|
|
404
844
|
connections=connections,
|
|
405
845
|
)
|
|
406
|
-
logger.debug("Request xml: {
|
|
846
|
+
logger.debug(f"Request xml: {redact_xml(xml_request[:1000])} ")
|
|
407
847
|
|
|
408
848
|
# Send the publishing request to server
|
|
409
849
|
try:
|
|
@@ -415,16 +855,38 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
415
855
|
|
|
416
856
|
if as_job:
|
|
417
857
|
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
418
|
-
logger.info("Published {
|
|
858
|
+
logger.info(f"Published {workbook_item.name} (JOB_ID: {new_job.id}")
|
|
419
859
|
return new_job
|
|
420
860
|
else:
|
|
421
861
|
new_workbook = WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
422
|
-
logger.info("Published {
|
|
862
|
+
logger.info(f"Published {workbook_item.name} (ID: {new_workbook.id})")
|
|
423
863
|
return new_workbook
|
|
424
864
|
|
|
425
865
|
# Populate workbook item's revisions
|
|
426
866
|
@api(version="2.3")
|
|
427
867
|
def populate_revisions(self, workbook_item: WorkbookItem) -> None:
|
|
868
|
+
"""
|
|
869
|
+
Populates (or gets) a list of revisions for a workbook.
|
|
870
|
+
|
|
871
|
+
You must first call this method to populate revisions before you can
|
|
872
|
+
iterate through the revisions.
|
|
873
|
+
|
|
874
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_workbook_revisions
|
|
875
|
+
|
|
876
|
+
Parameters
|
|
877
|
+
----------
|
|
878
|
+
workbook_item : WorkbookItem
|
|
879
|
+
The workbook item to populate revisions for.
|
|
880
|
+
|
|
881
|
+
Returns
|
|
882
|
+
-------
|
|
883
|
+
None
|
|
884
|
+
|
|
885
|
+
Raises
|
|
886
|
+
------
|
|
887
|
+
MissingRequiredFieldError
|
|
888
|
+
If the workbook item is missing an ID.
|
|
889
|
+
"""
|
|
428
890
|
if not workbook_item.id:
|
|
429
891
|
error = "Workbook item missing ID. Workbook must be retrieved from server first."
|
|
430
892
|
raise MissingRequiredFieldError(error)
|
|
@@ -433,12 +895,12 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
433
895
|
return self._get_workbook_revisions(workbook_item)
|
|
434
896
|
|
|
435
897
|
workbook_item._set_revisions(revisions_fetcher)
|
|
436
|
-
logger.info("Populated revisions for workbook (ID: {
|
|
898
|
+
logger.info(f"Populated revisions for workbook (ID: {workbook_item.id})")
|
|
437
899
|
|
|
438
900
|
def _get_workbook_revisions(
|
|
439
901
|
self, workbook_item: WorkbookItem, req_options: Optional["RequestOptions"] = None
|
|
440
|
-
) ->
|
|
441
|
-
url = "{
|
|
902
|
+
) -> list[RevisionItem]:
|
|
903
|
+
url = f"{self.baseurl}/{workbook_item.id}/revisions"
|
|
442
904
|
server_response = self.get_request(url, req_options)
|
|
443
905
|
revisions = RevisionItem.from_response(server_response.content, self.parent_srv.namespace, workbook_item)
|
|
444
906
|
return revisions
|
|
@@ -452,13 +914,47 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
452
914
|
filepath: Optional[PathOrFileW] = None,
|
|
453
915
|
include_extract: bool = True,
|
|
454
916
|
) -> PathOrFileW:
|
|
917
|
+
"""
|
|
918
|
+
Downloads a workbook revision to the specified directory (optional).
|
|
919
|
+
|
|
920
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#download_workbook_revision
|
|
921
|
+
|
|
922
|
+
Parameters
|
|
923
|
+
----------
|
|
924
|
+
workbook_id : str
|
|
925
|
+
The workbook ID.
|
|
926
|
+
|
|
927
|
+
revision_number : str | None
|
|
928
|
+
The revision number of the workbook. If None, the latest revision is
|
|
929
|
+
downloaded.
|
|
930
|
+
|
|
931
|
+
filepath : Path or File object, optional
|
|
932
|
+
Downloads the file to the location you specify. If no location is
|
|
933
|
+
specified, the file is downloaded to the current working directory.
|
|
934
|
+
The default is Filepath=None.
|
|
935
|
+
|
|
936
|
+
include_extract : bool, default True
|
|
937
|
+
Set to False to exclude the extract from the download. The default
|
|
938
|
+
is True.
|
|
939
|
+
|
|
940
|
+
Returns
|
|
941
|
+
-------
|
|
942
|
+
Path or File object
|
|
943
|
+
The path to the downloaded workbook or the file object.
|
|
944
|
+
|
|
945
|
+
Raises
|
|
946
|
+
------
|
|
947
|
+
ValueError
|
|
948
|
+
If the workbook ID is not defined.
|
|
949
|
+
"""
|
|
950
|
+
|
|
455
951
|
if not workbook_id:
|
|
456
952
|
error = "Workbook ID undefined."
|
|
457
953
|
raise ValueError(error)
|
|
458
954
|
if revision_number is None:
|
|
459
|
-
url = "{
|
|
955
|
+
url = f"{self.baseurl}/{workbook_id}/content"
|
|
460
956
|
else:
|
|
461
|
-
url = "{
|
|
957
|
+
url = f"{self.baseurl}/{workbook_id}/revisions/{revision_number}/content"
|
|
462
958
|
|
|
463
959
|
if not include_extract:
|
|
464
960
|
url += "?includeExtract=False"
|
|
@@ -480,37 +976,129 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
|
|
|
480
976
|
f.write(chunk)
|
|
481
977
|
return_path = os.path.abspath(download_path)
|
|
482
978
|
|
|
483
|
-
logger.info(
|
|
484
|
-
"Downloaded workbook revision {0} to {1} (ID: {2})".format(revision_number, return_path, workbook_id)
|
|
485
|
-
)
|
|
979
|
+
logger.info(f"Downloaded workbook revision {revision_number} to {return_path} (ID: {workbook_id})")
|
|
486
980
|
return return_path
|
|
487
981
|
|
|
488
982
|
@api(version="2.3")
|
|
489
983
|
def delete_revision(self, workbook_id: str, revision_number: str) -> None:
|
|
984
|
+
"""
|
|
985
|
+
Deletes a specific revision from a workbook on Tableau Server.
|
|
986
|
+
|
|
987
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_revisions.htm#remove_workbook_revision
|
|
988
|
+
|
|
989
|
+
Parameters
|
|
990
|
+
----------
|
|
991
|
+
workbook_id : str
|
|
992
|
+
The workbook ID.
|
|
993
|
+
|
|
994
|
+
revision_number : str
|
|
995
|
+
The revision number of the workbook to delete.
|
|
996
|
+
|
|
997
|
+
Returns
|
|
998
|
+
-------
|
|
999
|
+
None
|
|
1000
|
+
|
|
1001
|
+
Raises
|
|
1002
|
+
------
|
|
1003
|
+
ValueError
|
|
1004
|
+
If the workbook ID or revision number is not defined.
|
|
1005
|
+
"""
|
|
490
1006
|
if workbook_id is None or revision_number is None:
|
|
491
1007
|
raise ValueError
|
|
492
1008
|
url = "/".join([self.baseurl, workbook_id, "revisions", revision_number])
|
|
493
1009
|
|
|
494
1010
|
self.delete_request(url)
|
|
495
|
-
logger.info("Deleted single workbook revision (ID: {
|
|
1011
|
+
logger.info(f"Deleted single workbook revision (ID: {workbook_id}) (Revision: {revision_number})")
|
|
496
1012
|
|
|
497
1013
|
# a convenience method
|
|
498
1014
|
@api(version="2.8")
|
|
499
1015
|
def schedule_extract_refresh(
|
|
500
1016
|
self, schedule_id: str, item: WorkbookItem
|
|
501
|
-
) ->
|
|
1017
|
+
) -> list["AddResponse"]: # actually should return a task
|
|
1018
|
+
"""
|
|
1019
|
+
Adds a workbook to a schedule for extract refresh.
|
|
1020
|
+
|
|
1021
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#add_workbook_to_schedule
|
|
1022
|
+
|
|
1023
|
+
Parameters
|
|
1024
|
+
----------
|
|
1025
|
+
schedule_id : str
|
|
1026
|
+
The schedule ID.
|
|
1027
|
+
|
|
1028
|
+
item : WorkbookItem
|
|
1029
|
+
The workbook item to add to the schedule.
|
|
1030
|
+
|
|
1031
|
+
Returns
|
|
1032
|
+
-------
|
|
1033
|
+
list[AddResponse]
|
|
1034
|
+
The response from the server.
|
|
1035
|
+
"""
|
|
502
1036
|
return self.parent_srv.schedules.add_to_schedule(schedule_id, workbook=item)
|
|
503
1037
|
|
|
504
1038
|
@api(version="1.0")
|
|
505
|
-
def add_tags(self, item: Union[WorkbookItem, str], tags: Union[Iterable[str], str]) ->
|
|
1039
|
+
def add_tags(self, item: Union[WorkbookItem, str], tags: Union[Iterable[str], str]) -> set[str]:
|
|
1040
|
+
"""
|
|
1041
|
+
Adds tags to a workbook. One or more tags may be added at a time. If a
|
|
1042
|
+
tag already exists on the workbook, it will not be duplicated.
|
|
1043
|
+
|
|
1044
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#add_tags_to_workbook
|
|
1045
|
+
|
|
1046
|
+
Parameters
|
|
1047
|
+
----------
|
|
1048
|
+
item : WorkbookItem | str
|
|
1049
|
+
The workbook item or workbook ID to add tags to.
|
|
1050
|
+
|
|
1051
|
+
tags : Iterable[str] | str
|
|
1052
|
+
The tag or tags to add to the workbook. Tags can be a single tag or
|
|
1053
|
+
a list of tags.
|
|
1054
|
+
|
|
1055
|
+
Returns
|
|
1056
|
+
-------
|
|
1057
|
+
set[str]
|
|
1058
|
+
The set of tags added to the workbook.
|
|
1059
|
+
"""
|
|
506
1060
|
return super().add_tags(item, tags)
|
|
507
1061
|
|
|
508
1062
|
@api(version="1.0")
|
|
509
1063
|
def delete_tags(self, item: Union[WorkbookItem, str], tags: Union[Iterable[str], str]) -> None:
|
|
1064
|
+
"""
|
|
1065
|
+
Deletes tags from a workbook. One or more tags may be deleted at a time.
|
|
1066
|
+
|
|
1067
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#delete_tag_from_workbook
|
|
1068
|
+
|
|
1069
|
+
Parameters
|
|
1070
|
+
----------
|
|
1071
|
+
item : WorkbookItem | str
|
|
1072
|
+
The workbook item or workbook ID to delete tags from.
|
|
1073
|
+
|
|
1074
|
+
tags : Iterable[str] | str
|
|
1075
|
+
The tag or tags to delete from the workbook. Tags can be a single
|
|
1076
|
+
tag or a list of tags.
|
|
1077
|
+
|
|
1078
|
+
Returns
|
|
1079
|
+
-------
|
|
1080
|
+
None
|
|
1081
|
+
"""
|
|
510
1082
|
return super().delete_tags(item, tags)
|
|
511
1083
|
|
|
512
1084
|
@api(version="1.0")
|
|
513
1085
|
def update_tags(self, item: WorkbookItem) -> None:
|
|
1086
|
+
"""
|
|
1087
|
+
Updates the tags on a workbook. This method is used to update the tags
|
|
1088
|
+
on the server to match the tags on the workbook item. This method is a
|
|
1089
|
+
convenience method that calls add_tags and delete_tags to update the
|
|
1090
|
+
tags on the server.
|
|
1091
|
+
|
|
1092
|
+
Parameters
|
|
1093
|
+
----------
|
|
1094
|
+
item : WorkbookItem
|
|
1095
|
+
The workbook item to update the tags for. The tags on the workbook
|
|
1096
|
+
item will be used to update the tags on the server.
|
|
1097
|
+
|
|
1098
|
+
Returns
|
|
1099
|
+
-------
|
|
1100
|
+
None
|
|
1101
|
+
"""
|
|
514
1102
|
return super().update_tags(item)
|
|
515
1103
|
|
|
516
1104
|
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[WorkbookItem]:
|