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.
Files changed (94) hide show
  1. tableauserverclient/__init__.py +28 -22
  2. tableauserverclient/_version.py +3 -3
  3. tableauserverclient/config.py +5 -3
  4. tableauserverclient/models/column_item.py +1 -1
  5. tableauserverclient/models/connection_credentials.py +1 -1
  6. tableauserverclient/models/connection_item.py +6 -6
  7. tableauserverclient/models/custom_view_item.py +29 -6
  8. tableauserverclient/models/data_acceleration_report_item.py +2 -2
  9. tableauserverclient/models/data_alert_item.py +5 -5
  10. tableauserverclient/models/data_freshness_policy_item.py +6 -6
  11. tableauserverclient/models/database_item.py +3 -3
  12. tableauserverclient/models/datasource_item.py +10 -10
  13. tableauserverclient/models/dqw_item.py +1 -1
  14. tableauserverclient/models/favorites_item.py +5 -6
  15. tableauserverclient/models/fileupload_item.py +1 -1
  16. tableauserverclient/models/flow_item.py +6 -6
  17. tableauserverclient/models/flow_run_item.py +3 -3
  18. tableauserverclient/models/group_item.py +4 -4
  19. tableauserverclient/models/groupset_item.py +4 -4
  20. tableauserverclient/models/interval_item.py +9 -9
  21. tableauserverclient/models/job_item.py +8 -8
  22. tableauserverclient/models/linked_tasks_item.py +5 -5
  23. tableauserverclient/models/metric_item.py +5 -5
  24. tableauserverclient/models/pagination_item.py +1 -1
  25. tableauserverclient/models/permissions_item.py +12 -10
  26. tableauserverclient/models/project_item.py +35 -19
  27. tableauserverclient/models/property_decorators.py +12 -11
  28. tableauserverclient/models/reference_item.py +2 -2
  29. tableauserverclient/models/revision_item.py +3 -3
  30. tableauserverclient/models/schedule_item.py +2 -2
  31. tableauserverclient/models/server_info_item.py +26 -6
  32. tableauserverclient/models/site_item.py +69 -3
  33. tableauserverclient/models/subscription_item.py +3 -3
  34. tableauserverclient/models/table_item.py +1 -1
  35. tableauserverclient/models/tableau_auth.py +115 -5
  36. tableauserverclient/models/tableau_types.py +2 -2
  37. tableauserverclient/models/tag_item.py +3 -4
  38. tableauserverclient/models/task_item.py +4 -4
  39. tableauserverclient/models/user_item.py +47 -17
  40. tableauserverclient/models/view_item.py +11 -10
  41. tableauserverclient/models/virtual_connection_item.py +6 -5
  42. tableauserverclient/models/webhook_item.py +6 -6
  43. tableauserverclient/models/workbook_item.py +90 -12
  44. tableauserverclient/namespace.py +1 -1
  45. tableauserverclient/server/__init__.py +2 -1
  46. tableauserverclient/server/endpoint/auth_endpoint.py +65 -8
  47. tableauserverclient/server/endpoint/custom_views_endpoint.py +62 -18
  48. tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
  49. tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
  50. tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
  51. tableauserverclient/server/endpoint/datasources_endpoint.py +49 -54
  52. tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
  53. tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
  54. tableauserverclient/server/endpoint/endpoint.py +19 -21
  55. tableauserverclient/server/endpoint/exceptions.py +23 -7
  56. tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
  57. tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
  58. tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
  59. tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
  60. tableauserverclient/server/endpoint/flows_endpoint.py +30 -29
  61. tableauserverclient/server/endpoint/groups_endpoint.py +18 -17
  62. tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
  63. tableauserverclient/server/endpoint/jobs_endpoint.py +7 -7
  64. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
  65. tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
  66. tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
  67. tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
  68. tableauserverclient/server/endpoint/projects_endpoint.py +81 -30
  69. tableauserverclient/server/endpoint/resource_tagger.py +14 -13
  70. tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
  71. tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
  72. tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
  73. tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
  74. tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
  75. tableauserverclient/server/endpoint/tasks_endpoint.py +8 -8
  76. tableauserverclient/server/endpoint/users_endpoint.py +366 -19
  77. tableauserverclient/server/endpoint/views_endpoint.py +19 -18
  78. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
  79. tableauserverclient/server/endpoint/webhooks_endpoint.py +11 -11
  80. tableauserverclient/server/endpoint/workbooks_endpoint.py +647 -61
  81. tableauserverclient/server/filter.py +2 -2
  82. tableauserverclient/server/pager.py +5 -6
  83. tableauserverclient/server/query.py +68 -19
  84. tableauserverclient/server/request_factory.py +37 -36
  85. tableauserverclient/server/request_options.py +123 -145
  86. tableauserverclient/server/server.py +65 -9
  87. tableauserverclient/server/sort.py +2 -2
  88. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/METADATA +6 -6
  89. tableauserverclient-0.34.dist-info/RECORD +106 -0
  90. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/WHEEL +1 -1
  91. tableauserverclient-0.33.dist-info/RECORD +0 -106
  92. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/LICENSE +0 -0
  93. {tableauserverclient-0.33.dist-info → tableauserverclient-0.34.dist-info}/LICENSE.versioneer +0 -0
  94. {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(Workbooks, self).__init__(parent_srv)
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 "{0}/sites/{1}/workbooks".format(self.parent_srv.baseurl, self.parent_srv.site_id)
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) -> Tuple[List[WorkbookItem], PaginationItem]:
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: {0})".format(workbook_id))
90
- url = "{0}/{1}".format(self.baseurl, workbook_id)
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 = "{0}/{1}/refresh".format(self.baseurl, id_)
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[List["DatasourceItem"]] = None,
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 = "{0}/{1}/createExtract?encrypt={2}".format(self.baseurl, id_, encrypt)
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 = "{0}/{1}/deleteExtract".format(self.baseurl, id_)
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 = "{0}/{1}".format(self.baseurl, workbook_id)
237
+ url = f"{self.baseurl}/{workbook_id}"
137
238
  self.delete_request(url)
138
- logger.info("Deleted single workbook (ID: {0})".format(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 = "{0}/{1}".format(self.baseurl, workbook_item.id)
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: {0})".format(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
- url = "{0}/{1}/connections/{2}".format(self.baseurl, workbook_item.id, connection_item.id)
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() -> List[ViewItem]:
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: {0})".format(workbook_item.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) -> List[ViewItem]:
209
- url = "{0}/{1}/views".format(self.baseurl, workbook_item.id)
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: {0})".format(workbook_item.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
- ) -> List[ConnectionItem]:
236
- url = "{0}/{1}/connections".format(self.baseurl, workbook_item.id)
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: {0})".format(workbook_item.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 = "{0}/{1}/pdf".format(self.baseurl, workbook_item.id)
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: {0})".format(workbook_item.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 = "{0}/{1}/powerpoint".format(self.baseurl, workbook_item.id)
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: {0})".format(workbook_item.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 = "{0}/{1}/previewImage".format(self.baseurl, workbook_item.id)
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 IOError(error)
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 {}!".format(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 = "{}.{}".format(workbook_item.name, file_extension)
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 = "{0}?workbookType={1}".format(self.baseurl, file_extension)
803
+ url = f"{self.baseurl}?workbookType={file_extension}"
366
804
  if mode == self.parent_srv.PublishMode.Overwrite:
367
- url += "&{0}=true".format(mode.lower())
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 += "&{0}=true".format("asJob")
811
+ url += "&{}=true".format("asJob")
374
812
 
375
813
  if skip_connection_check:
376
- url += "&{0}=true".format("skipConnectionCheck")
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 {0} to server with chunking method (workbook over 64MB)".format(workbook_item.name))
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 = "{0}&uploadSessionId={1}".format(url, upload_session_id)
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 {0} to server".format(filename))
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: {0} ".format(redact_xml(xml_request[:1000])))
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 {0} (JOB_ID: {1}".format(workbook_item.name, new_job.id))
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 {0} (ID: {1})".format(workbook_item.name, new_workbook.id))
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: {0})".format(workbook_item.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
- ) -> List[RevisionItem]:
441
- url = "{0}/{1}/revisions".format(self.baseurl, workbook_item.id)
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 = "{0}/{1}/content".format(self.baseurl, workbook_id)
953
+ url = f"{self.baseurl}/{workbook_id}/content"
460
954
  else:
461
- url = "{0}/{1}/revisions/{2}/content".format(self.baseurl, workbook_id, revision_number)
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: {0}) (Revision: {1})".format(workbook_id, revision_number))
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
- ) -> List["AddResponse"]: # actually should return a task
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]) -> Set[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]: