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