tableauserverclient 0.34__py3-none-any.whl → 0.35__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. tableauserverclient/__init__.py +5 -1
  2. tableauserverclient/{_version.py → bin/_version.py} +3 -3
  3. tableauserverclient/models/connection_credentials.py +17 -1
  4. tableauserverclient/models/connection_item.py +38 -0
  5. tableauserverclient/models/custom_view_item.py +49 -5
  6. tableauserverclient/models/flow_item.py +49 -4
  7. tableauserverclient/models/group_item.py +40 -0
  8. tableauserverclient/models/job_item.py +65 -0
  9. tableauserverclient/models/project_item.py +39 -1
  10. tableauserverclient/models/task_item.py +30 -0
  11. tableauserverclient/models/view_item.py +55 -3
  12. tableauserverclient/models/webhook_item.py +33 -0
  13. tableauserverclient/models/workbook_item.py +26 -1
  14. tableauserverclient/server/endpoint/auth_endpoint.py +4 -2
  15. tableauserverclient/server/endpoint/custom_views_endpoint.py +197 -12
  16. tableauserverclient/server/endpoint/datasources_endpoint.py +13 -9
  17. tableauserverclient/server/endpoint/flows_endpoint.py +314 -0
  18. tableauserverclient/server/endpoint/groups_endpoint.py +325 -11
  19. tableauserverclient/server/endpoint/jobs_endpoint.py +109 -0
  20. tableauserverclient/server/endpoint/projects_endpoint.py +600 -0
  21. tableauserverclient/server/endpoint/tasks_endpoint.py +78 -0
  22. tableauserverclient/server/endpoint/views_endpoint.py +243 -2
  23. tableauserverclient/server/endpoint/webhooks_endpoint.py +77 -0
  24. tableauserverclient/server/endpoint/workbooks_endpoint.py +6 -4
  25. tableauserverclient/server/pager.py +24 -0
  26. tableauserverclient/server/request_factory.py +20 -1
  27. tableauserverclient/server/request_options.py +126 -2
  28. tableauserverclient/server/server.py +11 -1
  29. tableauserverclient/server/sort.py +14 -0
  30. {tableauserverclient-0.34.dist-info → tableauserverclient-0.35.dist-info}/METADATA +2 -2
  31. {tableauserverclient-0.34.dist-info → tableauserverclient-0.35.dist-info}/RECORD +35 -35
  32. {tableauserverclient-0.34.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
  33. {tableauserverclient-0.34.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
  34. {tableauserverclient-0.34.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
  35. {tableauserverclient-0.34.dist-info → tableauserverclient-0.35.dist-info}/top_level.txt +0 -0
@@ -31,6 +31,24 @@ class Tasks(Endpoint):
31
31
  def get(
32
32
  self, req_options: Optional["RequestOptions"] = None, task_type: str = TaskItem.Type.ExtractRefresh
33
33
  ) -> tuple[list[TaskItem], PaginationItem]:
34
+ """
35
+ Returns information about tasks on the specified site.
36
+
37
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#list_extract_refresh_tasks
38
+
39
+ Parameters
40
+ ----------
41
+ req_options : RequestOptions, optional
42
+ Options for the request, such as filtering, sorting, and pagination.
43
+
44
+ task_type : str, optional
45
+ The type of task to query. See TaskItem.Type for possible values.
46
+
47
+ Returns
48
+ -------
49
+ tuple[list[TaskItem], PaginationItem]
50
+
51
+ """
34
52
  if task_type == TaskItem.Type.DataAcceleration:
35
53
  self.parent_srv.assert_at_least_version("3.8", "Data Acceleration Tasks")
36
54
 
@@ -45,6 +63,20 @@ class Tasks(Endpoint):
45
63
 
46
64
  @api(version="2.6")
47
65
  def get_by_id(self, task_id: str) -> TaskItem:
66
+ """
67
+ Returns information about the specified task.
68
+
69
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#get_extract_refresh_task
70
+
71
+ Parameters
72
+ ----------
73
+ task_id : str
74
+ The ID of the task to query.
75
+
76
+ Returns
77
+ -------
78
+ TaskItem
79
+ """
48
80
  if not task_id:
49
81
  error = "No Task ID provided"
50
82
  raise ValueError(error)
@@ -59,6 +91,21 @@ class Tasks(Endpoint):
59
91
 
60
92
  @api(version="3.19")
61
93
  def create(self, extract_item: TaskItem) -> TaskItem:
94
+ """
95
+ Creates a custom schedule for an extract refresh on Tableau Cloud. For
96
+ Tableau Server, use the Schedules endpoint to create a schedule.
97
+
98
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#create_cloud_extract_refresh_task
99
+
100
+ Parameters
101
+ ----------
102
+ extract_item : TaskItem
103
+ The extract refresh task to create.
104
+
105
+ Returns
106
+ -------
107
+ TaskItem
108
+ """
62
109
  if not extract_item:
63
110
  error = "No extract refresh provided"
64
111
  raise ValueError(error)
@@ -70,6 +117,20 @@ class Tasks(Endpoint):
70
117
 
71
118
  @api(version="2.6")
72
119
  def run(self, task_item: TaskItem) -> bytes:
120
+ """
121
+ Runs the specified extract refresh task.
122
+
123
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#run_extract_refresh_task
124
+
125
+ Parameters
126
+ ----------
127
+ task_item : TaskItem
128
+ The task to run.
129
+
130
+ Returns
131
+ -------
132
+ bytes
133
+ """
73
134
  if not task_item.id:
74
135
  error = "Task item missing ID."
75
136
  raise MissingRequiredFieldError(error)
@@ -86,6 +147,23 @@ class Tasks(Endpoint):
86
147
  # Delete 1 task by id
87
148
  @api(version="3.6")
88
149
  def delete(self, task_id: str, task_type: str = TaskItem.Type.ExtractRefresh) -> None:
150
+ """
151
+ Deletes the specified extract refresh task on Tableau Server or Tableau Cloud.
152
+
153
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#delete_extract_refresh_task
154
+
155
+ Parameters
156
+ ----------
157
+ task_id : str
158
+ The ID of the task to delete.
159
+
160
+ task_type : str, default TaskItem.Type.ExtractRefresh
161
+ The type of task to query. See TaskItem.Type for possible values.
162
+
163
+ Returns
164
+ -------
165
+ None
166
+ """
89
167
  if task_type == TaskItem.Type.DataAcceleration:
90
168
  self.parent_srv.assert_at_least_version("3.8", "Data Acceleration Tasks")
91
169
 
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  from contextlib import closing
3
3
 
4
+ from tableauserverclient.models.permissions_item import PermissionsRule
4
5
  from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
5
6
  from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
6
7
  from tableauserverclient.server.endpoint.permissions_endpoint import _PermissionsEndpoint
@@ -25,6 +26,12 @@ if TYPE_CHECKING:
25
26
 
26
27
 
27
28
  class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
29
+ """
30
+ The Tableau Server Client provides methods for interacting with view
31
+ resources, or endpoints. These methods correspond to the endpoints for views
32
+ in the Tableau Server REST API.
33
+ """
34
+
28
35
  def __init__(self, parent_srv):
29
36
  super().__init__(parent_srv)
30
37
  self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
@@ -42,6 +49,24 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
42
49
  def get(
43
50
  self, req_options: Optional["RequestOptions"] = None, usage: bool = False
44
51
  ) -> tuple[list[ViewItem], PaginationItem]:
52
+ """
53
+ Returns the list of views on the site. Paginated endpoint.
54
+
55
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_views_for_site
56
+
57
+ Parameters
58
+ ----------
59
+ req_options: Optional[RequestOptions], default None
60
+ The request options for the request. These options can include
61
+ parameters such as page size and sorting.
62
+
63
+ usage: bool, default False
64
+ If True, includes usage statistics in the response.
65
+
66
+ Returns
67
+ -------
68
+ views: tuple[list[ViewItem], PaginationItem]
69
+ """
45
70
  logger.info("Querying all views on site")
46
71
  url = self.baseurl
47
72
  if usage:
@@ -53,6 +78,23 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
53
78
 
54
79
  @api(version="3.1")
55
80
  def get_by_id(self, view_id: str, usage: bool = False) -> ViewItem:
81
+ """
82
+ Returns the details of a specific view.
83
+
84
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_view
85
+
86
+ Parameters
87
+ ----------
88
+ view_id: str
89
+ The view ID.
90
+
91
+ usage: bool, default False
92
+ If True, includes usage statistics in the response.
93
+
94
+ Returns
95
+ -------
96
+ view_item: ViewItem
97
+ """
56
98
  if not view_id:
57
99
  error = "View item missing ID."
58
100
  raise MissingRequiredFieldError(error)
@@ -65,6 +107,24 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
65
107
 
66
108
  @api(version="2.0")
67
109
  def populate_preview_image(self, view_item: ViewItem) -> None:
110
+ """
111
+ Populates a preview image for the specified view.
112
+
113
+ This method gets the preview image (thumbnail) for the specified view
114
+ item. The method uses the id and workbook_id fields to query the preview
115
+ image. The method populates the preview_image for the view.
116
+
117
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_view_with_preview
118
+
119
+ Parameters
120
+ ----------
121
+ view_item: ViewItem
122
+ The view item for which to populate the preview image.
123
+
124
+ Returns
125
+ -------
126
+ None
127
+ """
68
128
  if not view_item.id or not view_item.workbook_id:
69
129
  error = "View item missing ID or workbook ID."
70
130
  raise MissingRequiredFieldError(error)
@@ -83,6 +143,27 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
83
143
 
84
144
  @api(version="2.5")
85
145
  def populate_image(self, view_item: ViewItem, req_options: Optional["ImageRequestOptions"] = None) -> None:
146
+ """
147
+ Populates the image of the specified view.
148
+
149
+ This method uses the id field to query the image, and populates the
150
+ image content as the image field.
151
+
152
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_view_image
153
+
154
+ Parameters
155
+ ----------
156
+ view_item: ViewItem
157
+ The view item for which to populate the image.
158
+
159
+ req_options: Optional[ImageRequestOptions], default None
160
+ Optional request options for the request. These options can include
161
+ parameters such as image resolution and max age.
162
+
163
+ Returns
164
+ -------
165
+ None
166
+ """
86
167
  if not view_item.id:
87
168
  error = "View item missing ID."
88
169
  raise MissingRequiredFieldError(error)
@@ -101,6 +182,26 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
101
182
 
102
183
  @api(version="2.7")
103
184
  def populate_pdf(self, view_item: ViewItem, req_options: Optional["PDFRequestOptions"] = None) -> None:
185
+ """
186
+ Populates the PDF content of the specified view.
187
+
188
+ This method populates a PDF with image(s) of the view you specify.
189
+
190
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_view_pdf
191
+
192
+ Parameters
193
+ ----------
194
+ view_item: ViewItem
195
+ The view item for which to populate the PDF.
196
+
197
+ req_options: Optional[PDFRequestOptions], default None
198
+ Optional request options for the request. These options can include
199
+ parameters such as orientation and paper size.
200
+
201
+ Returns
202
+ -------
203
+ None
204
+ """
104
205
  if not view_item.id:
105
206
  error = "View item missing ID."
106
207
  raise MissingRequiredFieldError(error)
@@ -119,6 +220,27 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
119
220
 
120
221
  @api(version="2.7")
121
222
  def populate_csv(self, view_item: ViewItem, req_options: Optional["CSVRequestOptions"] = None) -> None:
223
+ """
224
+ Populates the CSV data of the specified view.
225
+
226
+ This method uses the id field to query the CSV data, and populates the
227
+ data as the csv field.
228
+
229
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_view_data
230
+
231
+ Parameters
232
+ ----------
233
+ view_item: ViewItem
234
+ The view item for which to populate the CSV data.
235
+
236
+ req_options: Optional[CSVRequestOptions], default None
237
+ Optional request options for the request. These options can include
238
+ parameters such as view filters and max age.
239
+
240
+ Returns
241
+ -------
242
+ None
243
+ """
122
244
  if not view_item.id:
123
245
  error = "View item missing ID."
124
246
  raise MissingRequiredFieldError(error)
@@ -137,6 +259,27 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
137
259
 
138
260
  @api(version="3.8")
139
261
  def populate_excel(self, view_item: ViewItem, req_options: Optional["ExcelRequestOptions"] = None) -> None:
262
+ """
263
+ Populates the Excel data of the specified view.
264
+
265
+ This method uses the id field to query the Excel data, and populates the
266
+ data as the Excel field.
267
+
268
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#download_view_excel
269
+
270
+ Parameters
271
+ ----------
272
+ view_item: ViewItem
273
+ The view item for which to populate the Excel data.
274
+
275
+ req_options: Optional[ExcelRequestOptions], default None
276
+ Optional request options for the request. These options can include
277
+ parameters such as view filters and max age.
278
+
279
+ Returns
280
+ -------
281
+ None
282
+ """
140
283
  if not view_item.id:
141
284
  error = "View item missing ID."
142
285
  raise MissingRequiredFieldError(error)
@@ -155,18 +298,66 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
155
298
 
156
299
  @api(version="3.2")
157
300
  def populate_permissions(self, item: ViewItem) -> None:
301
+ """
302
+ Returns a list of permissions for the specific view.
303
+
304
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_permissions.htm#query_view_permissions
305
+
306
+ Parameters
307
+ ----------
308
+ item: ViewItem
309
+ The view item for which to populate the permissions.
310
+
311
+ Returns
312
+ -------
313
+ None
314
+ """
158
315
  self._permissions.populate(item)
159
316
 
160
317
  @api(version="3.2")
161
- def update_permissions(self, resource, rules):
318
+ def update_permissions(self, resource: ViewItem, rules: list[PermissionsRule]) -> list[PermissionsRule]:
319
+ """ """
162
320
  return self._permissions.update(resource, rules)
163
321
 
164
322
  @api(version="3.2")
165
- def delete_permission(self, item, capability_item):
323
+ def delete_permission(self, item: ViewItem, capability_item: PermissionsRule) -> None:
324
+ """
325
+ Deletes permission to the specified view (also known as a sheet) for a
326
+ Tableau Server user or group.
327
+
328
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_permissions.htm#delete_view_permission
329
+
330
+ Parameters
331
+ ----------
332
+ item: ViewItem
333
+ The view item for which to delete the permission.
334
+
335
+ capability_item: PermissionsRule
336
+ The permission rule to delete.
337
+
338
+ Returns
339
+ -------
340
+ None
341
+ """
166
342
  return self._permissions.delete(item, capability_item)
167
343
 
168
344
  # Update view. Currently only tags can be updated
169
345
  def update(self, view_item: ViewItem) -> ViewItem:
346
+ """
347
+ Updates the tags for the specified view. All other fields are managed
348
+ through the WorkbookItem object.
349
+
350
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#add_tags_to_view
351
+
352
+ Parameters
353
+ ----------
354
+ view_item: ViewItem
355
+ The view item for which to update tags.
356
+
357
+ Returns
358
+ -------
359
+ ViewItem
360
+ """
170
361
  if not view_item.id:
171
362
  error = "View item missing ID. View must be retrieved from server first."
172
363
  raise MissingRequiredFieldError(error)
@@ -178,14 +369,64 @@ class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
178
369
 
179
370
  @api(version="1.0")
180
371
  def add_tags(self, item: Union[ViewItem, str], tags: Union[Iterable[str], str]) -> set[str]:
372
+ """
373
+ Adds tags to the specified view.
374
+
375
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#add_tags_to_view
376
+
377
+ Parameters
378
+ ----------
379
+ item: Union[ViewItem, str]
380
+ The view item or view ID to which to add tags.
381
+
382
+ tags: Union[Iterable[str], str]
383
+ The tags to add to the view.
384
+
385
+ Returns
386
+ -------
387
+ set[str]
388
+
389
+ """
181
390
  return super().add_tags(item, tags)
182
391
 
183
392
  @api(version="1.0")
184
393
  def delete_tags(self, item: Union[ViewItem, str], tags: Union[Iterable[str], str]) -> None:
394
+ """
395
+ Deletes tags from the specified view.
396
+
397
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#delete_tags_from_view
398
+
399
+ Parameters
400
+ ----------
401
+ item: Union[ViewItem, str]
402
+ The view item or view ID from which to delete tags.
403
+
404
+ tags: Union[Iterable[str], str]
405
+ The tags to delete from the view.
406
+
407
+ Returns
408
+ -------
409
+ None
410
+ """
185
411
  return super().delete_tags(item, tags)
186
412
 
187
413
  @api(version="1.0")
188
414
  def update_tags(self, item: ViewItem) -> None:
415
+ """
416
+ Updates the tags for the specified view. Any changes to the tags must
417
+ be made by editing the tags attribute of the view item.
418
+
419
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#add_tags_to_view
420
+
421
+ Parameters
422
+ ----------
423
+ item: ViewItem
424
+ The view item for which to update tags.
425
+
426
+ Returns
427
+ -------
428
+ None
429
+ """
189
430
  return super().update_tags(item)
190
431
 
191
432
  def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[ViewItem]:
@@ -23,6 +23,21 @@ class Webhooks(Endpoint):
23
23
 
24
24
  @api(version="3.6")
25
25
  def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[WebhookItem], PaginationItem]:
26
+ """
27
+ Returns a list of all webhooks on the site.
28
+
29
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#list_webhooks_for_site
30
+
31
+ Parameters
32
+ ----------
33
+ req_options : Optional[RequestOptions]
34
+ Filter and sorting options for the request.
35
+
36
+ Returns
37
+ -------
38
+ tuple[list[WebhookItem], PaginationItem]
39
+ A tuple of the list of webhooks and pagination item
40
+ """
26
41
  logger.info("Querying all Webhooks on site")
27
42
  url = self.baseurl
28
43
  server_response = self.get_request(url, req_options)
@@ -32,6 +47,21 @@ class Webhooks(Endpoint):
32
47
 
33
48
  @api(version="3.6")
34
49
  def get_by_id(self, webhook_id: str) -> WebhookItem:
50
+ """
51
+ Returns information about a specified Webhook.
52
+
53
+ Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#get_webhook
54
+
55
+ Parameters
56
+ ----------
57
+ webhook_id : str
58
+ The ID of the webhook to query.
59
+
60
+ Returns
61
+ -------
62
+ WebhookItem
63
+ An object containing information about the webhook.
64
+ """
35
65
  if not webhook_id:
36
66
  error = "Webhook ID undefined."
37
67
  raise ValueError(error)
@@ -42,6 +72,20 @@ class Webhooks(Endpoint):
42
72
 
43
73
  @api(version="3.6")
44
74
  def delete(self, webhook_id: str) -> None:
75
+ """
76
+ Deletes a specified webhook.
77
+
78
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#delete_webhook
79
+
80
+ Parameters
81
+ ----------
82
+ webhook_id : str
83
+ The ID of the webhook to delete.
84
+
85
+ Returns
86
+ -------
87
+ None
88
+ """
45
89
  if not webhook_id:
46
90
  error = "Webhook ID undefined."
47
91
  raise ValueError(error)
@@ -51,6 +95,21 @@ class Webhooks(Endpoint):
51
95
 
52
96
  @api(version="3.6")
53
97
  def create(self, webhook_item: WebhookItem) -> WebhookItem:
98
+ """
99
+ Creates a new webhook on the site.
100
+
101
+ REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#create_webhook
102
+
103
+ Parameters
104
+ ----------
105
+ webhook_item : WebhookItem
106
+ The webhook item to create.
107
+
108
+ Returns
109
+ -------
110
+ WebhookItem
111
+ An object containing information about the created webhook
112
+ """
54
113
  url = self.baseurl
55
114
  create_req = RequestFactory.Webhook.create_req(webhook_item)
56
115
  server_response = self.post_request(url, create_req)
@@ -61,6 +120,24 @@ class Webhooks(Endpoint):
61
120
 
62
121
  @api(version="3.6")
63
122
  def test(self, webhook_id: str):
123
+ """
124
+ Tests the specified webhook. Sends an empty payload to the configured
125
+ destination URL of the webhook and returns the response from the server.
126
+ This is useful for testing, to ensure that things are being sent from
127
+ Tableau and received back as expected.
128
+
129
+ Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref.htm#test_webhook
130
+
131
+ Parameters
132
+ ----------
133
+ webhook_id : str
134
+ The ID of the webhook to test.
135
+
136
+ Returns
137
+ -------
138
+ XML Response
139
+
140
+ """
64
141
  if not webhook_id:
65
142
  error = "Webhook ID undefined."
66
143
  raise ValueError(error)
@@ -118,7 +118,7 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
118
118
  return WorkbookItem.from_response(server_response.content, self.parent_srv.namespace)[0]
119
119
 
120
120
  @api(version="2.8")
121
- def refresh(self, workbook_item: Union[WorkbookItem, str]) -> JobItem:
121
+ def refresh(self, workbook_item: Union[WorkbookItem, str], incremental: bool = False) -> JobItem:
122
122
  """
123
123
  Refreshes the extract of an existing workbook.
124
124
 
@@ -126,6 +126,8 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
126
126
  ----------
127
127
  workbook_item : WorkbookItem | str
128
128
  The workbook item or workbook ID.
129
+ incremental: bool
130
+ Whether to do a full refresh or incremental refresh of the extract data
129
131
 
130
132
  Returns
131
133
  -------
@@ -134,8 +136,8 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
134
136
  """
135
137
  id_ = getattr(workbook_item, "id", workbook_item)
136
138
  url = f"{self.baseurl}/{id_}/refresh"
137
- empty_req = RequestFactory.Empty.empty_req()
138
- server_response = self.post_request(url, empty_req)
139
+ refresh_req = RequestFactory.Task.refresh_req(incremental)
140
+ server_response = self.post_request(url, refresh_req)
139
141
  new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
140
142
  return new_job
141
143
 
@@ -280,7 +282,7 @@ class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
280
282
  if include_view_acceleration_status:
281
283
  url += "?includeViewAccelerationStatus=True"
282
284
 
283
- update_req = RequestFactory.Workbook.update_req(workbook_item)
285
+ update_req = RequestFactory.Workbook.update_req(workbook_item, self.parent_srv)
284
286
  server_response = self.put_request(url, update_req)
285
287
  logger.info(f"Updated workbook item (ID: {workbook_item.id})")
286
288
  updated_workbook = copy.copy(workbook_item)
@@ -27,6 +27,30 @@ class Pager(Iterable[T]):
27
27
  (users in a group, views in a workbook, etc) by passing a different endpoint.
28
28
 
29
29
  Will loop over anything that returns (list[ModelItem], PaginationItem).
30
+
31
+ Will make a copy of the `RequestOptions` object passed in so it can be reused.
32
+
33
+ Makes a call to the Server for each page of items, then yields each item in the list.
34
+
35
+ Parameters
36
+ ----------
37
+ endpoint: CallableEndpoint[T] or Endpoint[T]
38
+ The endpoint to call to get the items. Can be a callable or an Endpoint object.
39
+ Expects a tuple of (list[T], PaginationItem) to be returned.
40
+
41
+ request_opts: RequestOptions, optional
42
+ The request options to pass to the endpoint. If not provided, will use default RequestOptions.
43
+ Filters, sorts, page size, starting page number, etc can be set here.
44
+
45
+ Yields
46
+ ------
47
+ T
48
+ The items returned from the endpoint.
49
+
50
+ Raises
51
+ ------
52
+ ValueError
53
+ If the endpoint is not a callable or an Endpoint object.
30
54
  """
31
55
 
32
56
  def __init__(
@@ -958,9 +958,15 @@ class WorkbookRequest:
958
958
  views_element = ET.SubElement(workbook_element, "views")
959
959
  for view_name in workbook_item.hidden_views:
960
960
  _add_hiddenview_element(views_element, view_name)
961
+
962
+ if workbook_item.thumbnails_user_id is not None:
963
+ workbook_element.attrib["thumbnailsUserId"] = workbook_item.thumbnails_user_id
964
+ elif workbook_item.thumbnails_group_id is not None:
965
+ workbook_element.attrib["thumbnailsGroupId"] = workbook_item.thumbnails_group_id
966
+
961
967
  return ET.tostring(xml_request)
962
968
 
963
- def update_req(self, workbook_item):
969
+ def update_req(self, workbook_item, parent_srv: Optional["Server"] = None):
964
970
  xml_request = ET.Element("tsRequest")
965
971
  workbook_element = ET.SubElement(xml_request, "workbook")
966
972
  if workbook_item.name:
@@ -973,6 +979,12 @@ class WorkbookRequest:
973
979
  if workbook_item.owner_id:
974
980
  owner_element = ET.SubElement(workbook_element, "owner")
975
981
  owner_element.attrib["id"] = workbook_item.owner_id
982
+ if (
983
+ workbook_item.description is not None
984
+ and parent_srv is not None
985
+ and parent_srv.check_at_least_version("3.21")
986
+ ):
987
+ workbook_element.attrib["description"] = workbook_item.description
976
988
  if workbook_item._views is not None:
977
989
  views_element = ET.SubElement(workbook_element, "views")
978
990
  for view in workbook_item.views:
@@ -1105,6 +1117,13 @@ class TaskRequest:
1105
1117
  # Send an empty tsRequest
1106
1118
  pass
1107
1119
 
1120
+ @_tsrequest_wrapped
1121
+ def refresh_req(self, xml_request: ET.Element, incremental: bool = False) -> bytes:
1122
+ task_element = ET.SubElement(xml_request, "extractRefresh")
1123
+ if incremental:
1124
+ task_element.attrib["incremental"] = "true"
1125
+ return ET.tostring(xml_request)
1126
+
1108
1127
  @_tsrequest_wrapped
1109
1128
  def create_extract_req(self, xml_request: ET.Element, extract_item: "TaskItem") -> bytes:
1110
1129
  extract_element = ET.SubElement(xml_request, "extractRefresh")