tableauserverclient 0.33__py3-none-any.whl → 0.35__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tableauserverclient/__init__.py +33 -23
- tableauserverclient/{_version.py → bin/_version.py} +3 -3
- tableauserverclient/config.py +5 -3
- tableauserverclient/models/column_item.py +1 -1
- tableauserverclient/models/connection_credentials.py +18 -2
- tableauserverclient/models/connection_item.py +44 -6
- tableauserverclient/models/custom_view_item.py +78 -11
- tableauserverclient/models/data_acceleration_report_item.py +2 -2
- tableauserverclient/models/data_alert_item.py +5 -5
- tableauserverclient/models/data_freshness_policy_item.py +6 -6
- tableauserverclient/models/database_item.py +3 -3
- tableauserverclient/models/datasource_item.py +10 -10
- tableauserverclient/models/dqw_item.py +1 -1
- tableauserverclient/models/favorites_item.py +5 -6
- tableauserverclient/models/fileupload_item.py +1 -1
- tableauserverclient/models/flow_item.py +54 -9
- tableauserverclient/models/flow_run_item.py +3 -3
- tableauserverclient/models/group_item.py +44 -4
- tableauserverclient/models/groupset_item.py +4 -4
- tableauserverclient/models/interval_item.py +9 -9
- tableauserverclient/models/job_item.py +73 -8
- tableauserverclient/models/linked_tasks_item.py +5 -5
- tableauserverclient/models/metric_item.py +5 -5
- tableauserverclient/models/pagination_item.py +1 -1
- tableauserverclient/models/permissions_item.py +12 -10
- tableauserverclient/models/project_item.py +73 -19
- tableauserverclient/models/property_decorators.py +12 -11
- tableauserverclient/models/reference_item.py +2 -2
- tableauserverclient/models/revision_item.py +3 -3
- tableauserverclient/models/schedule_item.py +2 -2
- tableauserverclient/models/server_info_item.py +26 -6
- tableauserverclient/models/site_item.py +69 -3
- tableauserverclient/models/subscription_item.py +3 -3
- tableauserverclient/models/table_item.py +1 -1
- tableauserverclient/models/tableau_auth.py +115 -5
- tableauserverclient/models/tableau_types.py +2 -2
- tableauserverclient/models/tag_item.py +3 -4
- tableauserverclient/models/task_item.py +34 -4
- tableauserverclient/models/user_item.py +47 -17
- tableauserverclient/models/view_item.py +66 -13
- tableauserverclient/models/virtual_connection_item.py +6 -5
- tableauserverclient/models/webhook_item.py +39 -6
- tableauserverclient/models/workbook_item.py +116 -13
- tableauserverclient/namespace.py +1 -1
- tableauserverclient/server/__init__.py +2 -1
- tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
- tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
- tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
- tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
- tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
- tableauserverclient/server/endpoint/datasources_endpoint.py +61 -62
- tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
- tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
- tableauserverclient/server/endpoint/endpoint.py +19 -21
- tableauserverclient/server/endpoint/exceptions.py +23 -7
- tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
- tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flows_endpoint.py +344 -29
- tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
- tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
- tableauserverclient/server/endpoint/jobs_endpoint.py +116 -7
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
- tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
- tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
- tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
- tableauserverclient/server/endpoint/projects_endpoint.py +681 -30
- tableauserverclient/server/endpoint/resource_tagger.py +14 -13
- tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
- tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
- tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
- tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
- tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
- tableauserverclient/server/endpoint/tasks_endpoint.py +86 -8
- tableauserverclient/server/endpoint/users_endpoint.py +366 -19
- tableauserverclient/server/endpoint/views_endpoint.py +262 -20
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
- tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
- tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
- tableauserverclient/server/filter.py +2 -2
- tableauserverclient/server/pager.py +29 -6
- tableauserverclient/server/query.py +68 -19
- tableauserverclient/server/request_factory.py +57 -37
- tableauserverclient/server/request_options.py +243 -141
- tableauserverclient/server/server.py +76 -10
- tableauserverclient/server/sort.py +16 -2
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
- tableauserverclient-0.35.dist-info/RECORD +106 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
- tableauserverclient-0.33.dist-info/RECORD +0 -106
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
from contextlib import closing
|
|
4
5
|
from pathlib import Path
|
|
5
|
-
from typing import
|
|
6
|
+
from typing import Optional, Union, TYPE_CHECKING
|
|
7
|
+
from collections.abc import Iterator
|
|
6
8
|
|
|
7
|
-
from tableauserverclient.config import BYTES_PER_MB,
|
|
9
|
+
from tableauserverclient.config import BYTES_PER_MB, config
|
|
8
10
|
from tableauserverclient.filesys_helpers import get_file_object_size
|
|
9
11
|
from tableauserverclient.server.endpoint.endpoint import QuerysetEndpoint, api
|
|
10
12
|
from tableauserverclient.server.endpoint.exceptions import MissingRequiredFieldError
|
|
11
13
|
from tableauserverclient.models import CustomViewItem, PaginationItem
|
|
12
|
-
from tableauserverclient.server import
|
|
14
|
+
from tableauserverclient.server import (
|
|
15
|
+
RequestFactory,
|
|
16
|
+
RequestOptions,
|
|
17
|
+
ImageRequestOptions,
|
|
18
|
+
PDFRequestOptions,
|
|
19
|
+
CSVRequestOptions,
|
|
20
|
+
)
|
|
13
21
|
|
|
14
22
|
from tableauserverclient.helpers.logging import logger
|
|
15
23
|
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from tableauserverclient.server.query import QuerySet
|
|
26
|
+
|
|
16
27
|
"""
|
|
17
28
|
Get a list of custom views on a site
|
|
18
29
|
get the details of a custom view
|
|
@@ -33,29 +44,41 @@ io_types_w = (io.BufferedWriter, io.BytesIO)
|
|
|
33
44
|
|
|
34
45
|
class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
35
46
|
def __init__(self, parent_srv):
|
|
36
|
-
super(
|
|
47
|
+
super().__init__(parent_srv)
|
|
37
48
|
|
|
38
49
|
@property
|
|
39
50
|
def baseurl(self) -> str:
|
|
40
|
-
return "{
|
|
51
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/customviews"
|
|
41
52
|
|
|
42
53
|
@property
|
|
43
54
|
def expurl(self) -> str:
|
|
44
55
|
return f"{self.parent_srv._server_address}/api/exp/sites/{self.parent_srv.site_id}/customviews"
|
|
45
56
|
|
|
46
|
-
"""
|
|
47
|
-
If the request has no filter parameters: Administrators will see all custom views.
|
|
48
|
-
Other users will see only custom views that they own.
|
|
49
|
-
If the filter parameters include ownerId: Users will see only custom views that they own.
|
|
50
|
-
If the filter parameters include viewId and/or workbookId, and don't include ownerId:
|
|
51
|
-
Users will see those custom views that they have Write and WebAuthoring permissions for.
|
|
52
|
-
If site user visibility is not set to Limited, the Users will see those custom views that are "public",
|
|
53
|
-
meaning the value of their shared attribute is true.
|
|
54
|
-
If site user visibility is set to Limited, ????
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
57
|
@api(version="3.18")
|
|
58
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
58
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[CustomViewItem], PaginationItem]:
|
|
59
|
+
"""
|
|
60
|
+
Get a list of custom views on a site.
|
|
61
|
+
|
|
62
|
+
If the request has no filter parameters: Administrators will see all custom views.
|
|
63
|
+
Other users will see only custom views that they own.
|
|
64
|
+
If the filter parameters include ownerId: Users will see only custom views that they own.
|
|
65
|
+
If the filter parameters include viewId and/or workbookId, and don't include ownerId:
|
|
66
|
+
Users will see those custom views that they have Write and WebAuthoring permissions for.
|
|
67
|
+
If site user visibility is not set to Limited, the Users will see those custom views that are "public",
|
|
68
|
+
meaning the value of their shared attribute is true.
|
|
69
|
+
If site user visibility is set to Limited, ????
|
|
70
|
+
|
|
71
|
+
Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#list_custom_views
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
req_options : RequestOptions, optional
|
|
76
|
+
Filtering options for the request, by default None
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
tuple[list[CustomViewItem], PaginationItem]
|
|
81
|
+
"""
|
|
59
82
|
logger.info("Querying all custom views on site")
|
|
60
83
|
url = self.baseurl
|
|
61
84
|
server_response = self.get_request(url, req_options)
|
|
@@ -65,16 +88,50 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
65
88
|
|
|
66
89
|
@api(version="3.18")
|
|
67
90
|
def get_by_id(self, view_id: str) -> Optional[CustomViewItem]:
|
|
91
|
+
"""
|
|
92
|
+
Get the details of a specific custom view.
|
|
93
|
+
|
|
94
|
+
Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_custom_view
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
view_id : str
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
Optional[CustomViewItem]
|
|
103
|
+
"""
|
|
68
104
|
if not view_id:
|
|
69
105
|
error = "Custom view item missing ID."
|
|
70
106
|
raise MissingRequiredFieldError(error)
|
|
71
|
-
logger.info("Querying custom view (ID: {
|
|
72
|
-
url = "{
|
|
107
|
+
logger.info(f"Querying custom view (ID: {view_id})")
|
|
108
|
+
url = f"{self.baseurl}/{view_id}"
|
|
73
109
|
server_response = self.get_request(url)
|
|
74
110
|
return CustomViewItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
75
111
|
|
|
76
112
|
@api(version="3.18")
|
|
77
113
|
def populate_image(self, view_item: CustomViewItem, req_options: Optional["ImageRequestOptions"] = None) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Populate the image of a custom view.
|
|
116
|
+
|
|
117
|
+
Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_custom_view_image
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
view_item : CustomViewItem
|
|
122
|
+
|
|
123
|
+
req_options : ImageRequestOptions, optional
|
|
124
|
+
Options to customize the image returned, by default None
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
None
|
|
129
|
+
|
|
130
|
+
Raises
|
|
131
|
+
------
|
|
132
|
+
MissingRequiredFieldError
|
|
133
|
+
If the view_item is missing an ID
|
|
134
|
+
"""
|
|
78
135
|
if not view_item.id:
|
|
79
136
|
error = "Custom View item missing ID."
|
|
80
137
|
raise MissingRequiredFieldError(error)
|
|
@@ -83,20 +140,111 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
83
140
|
return self._get_view_image(view_item, req_options)
|
|
84
141
|
|
|
85
142
|
view_item._set_image(image_fetcher)
|
|
86
|
-
logger.info("Populated image for custom view (ID: {
|
|
143
|
+
logger.info(f"Populated image for custom view (ID: {view_item.id})")
|
|
87
144
|
|
|
88
145
|
def _get_view_image(self, view_item: CustomViewItem, req_options: Optional["ImageRequestOptions"]) -> bytes:
|
|
89
|
-
url = "{
|
|
146
|
+
url = f"{self.baseurl}/{view_item.id}/image"
|
|
90
147
|
server_response = self.get_request(url, req_options)
|
|
91
148
|
image = server_response.content
|
|
92
149
|
return image
|
|
93
150
|
|
|
94
|
-
""
|
|
95
|
-
|
|
96
|
-
|
|
151
|
+
@api(version="3.23")
|
|
152
|
+
def populate_pdf(self, custom_view_item: CustomViewItem, req_options: Optional["PDFRequestOptions"] = None) -> None:
|
|
153
|
+
"""
|
|
154
|
+
Populate the PDF of a custom view.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
custom_view_item : CustomViewItem
|
|
159
|
+
The custom view item to populate the PDF for.
|
|
160
|
+
|
|
161
|
+
req_options : PDFRequestOptions, optional
|
|
162
|
+
Options to customize the PDF returned, by default None
|
|
163
|
+
|
|
164
|
+
Returns
|
|
165
|
+
-------
|
|
166
|
+
None
|
|
167
|
+
|
|
168
|
+
Raises
|
|
169
|
+
------
|
|
170
|
+
MissingRequiredFieldError
|
|
171
|
+
If the custom view item is missing an ID
|
|
172
|
+
"""
|
|
173
|
+
if not custom_view_item.id:
|
|
174
|
+
error = "Custom View item missing ID."
|
|
175
|
+
raise MissingRequiredFieldError(error)
|
|
176
|
+
|
|
177
|
+
def pdf_fetcher():
|
|
178
|
+
return self._get_custom_view_pdf(custom_view_item, req_options)
|
|
179
|
+
|
|
180
|
+
custom_view_item._set_pdf(pdf_fetcher)
|
|
181
|
+
logger.info(f"Populated pdf for custom view (ID: {custom_view_item.id})")
|
|
182
|
+
|
|
183
|
+
def _get_custom_view_pdf(
|
|
184
|
+
self, custom_view_item: CustomViewItem, req_options: Optional["PDFRequestOptions"]
|
|
185
|
+
) -> bytes:
|
|
186
|
+
url = f"{self.baseurl}/{custom_view_item.id}/pdf"
|
|
187
|
+
server_response = self.get_request(url, req_options)
|
|
188
|
+
pdf = server_response.content
|
|
189
|
+
return pdf
|
|
190
|
+
|
|
191
|
+
@api(version="3.23")
|
|
192
|
+
def populate_csv(self, custom_view_item: CustomViewItem, req_options: Optional["CSVRequestOptions"] = None) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Populate the CSV of a custom view.
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
custom_view_item : CustomViewItem
|
|
199
|
+
The custom view item to populate the CSV for.
|
|
200
|
+
|
|
201
|
+
req_options : CSVRequestOptions, optional
|
|
202
|
+
Options to customize the CSV returned, by default None
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
None
|
|
207
|
+
|
|
208
|
+
Raises
|
|
209
|
+
------
|
|
210
|
+
MissingRequiredFieldError
|
|
211
|
+
If the custom view item is missing an ID
|
|
212
|
+
"""
|
|
213
|
+
if not custom_view_item.id:
|
|
214
|
+
error = "Custom View item missing ID."
|
|
215
|
+
raise MissingRequiredFieldError(error)
|
|
216
|
+
|
|
217
|
+
def csv_fetcher():
|
|
218
|
+
return self._get_custom_view_csv(custom_view_item, req_options)
|
|
219
|
+
|
|
220
|
+
custom_view_item._set_csv(csv_fetcher)
|
|
221
|
+
logger.info(f"Populated csv for custom view (ID: {custom_view_item.id})")
|
|
222
|
+
|
|
223
|
+
def _get_custom_view_csv(
|
|
224
|
+
self, custom_view_item: CustomViewItem, req_options: Optional["CSVRequestOptions"]
|
|
225
|
+
) -> Iterator[bytes]:
|
|
226
|
+
url = f"{self.baseurl}/{custom_view_item.id}/data"
|
|
227
|
+
|
|
228
|
+
with closing(self.get_request(url, request_object=req_options, parameters={"stream": True})) as server_response:
|
|
229
|
+
yield from server_response.iter_content(1024)
|
|
97
230
|
|
|
98
231
|
@api(version="3.18")
|
|
99
232
|
def update(self, view_item: CustomViewItem) -> Optional[CustomViewItem]:
|
|
233
|
+
"""
|
|
234
|
+
Updates the name, owner, or shared status of a custom view.
|
|
235
|
+
|
|
236
|
+
Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#update_custom_view
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
view_item : CustomViewItem
|
|
241
|
+
The custom view item to update.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
Optional[CustomViewItem]
|
|
246
|
+
The updated custom view item.
|
|
247
|
+
"""
|
|
100
248
|
if not view_item.id:
|
|
101
249
|
error = "Custom view item missing ID."
|
|
102
250
|
raise MissingRequiredFieldError(error)
|
|
@@ -105,24 +253,64 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
105
253
|
return view_item
|
|
106
254
|
|
|
107
255
|
# Update the custom view owner or name
|
|
108
|
-
url = "{
|
|
256
|
+
url = f"{self.baseurl}/{view_item.id}"
|
|
109
257
|
update_req = RequestFactory.CustomView.update_req(view_item)
|
|
110
258
|
server_response = self.put_request(url, update_req)
|
|
111
|
-
logger.info("Updated custom view (ID: {
|
|
259
|
+
logger.info(f"Updated custom view (ID: {view_item.id})")
|
|
112
260
|
return CustomViewItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
113
261
|
|
|
114
262
|
# Delete 1 view by id
|
|
115
263
|
@api(version="3.19")
|
|
116
264
|
def delete(self, view_id: str) -> None:
|
|
265
|
+
"""
|
|
266
|
+
Deletes a single custom view by ID.
|
|
267
|
+
|
|
268
|
+
Rest API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#delete_custom_view
|
|
269
|
+
|
|
270
|
+
Parameters
|
|
271
|
+
----------
|
|
272
|
+
view_id : str
|
|
273
|
+
The ID of the custom view to delete.
|
|
274
|
+
|
|
275
|
+
Returns
|
|
276
|
+
-------
|
|
277
|
+
None
|
|
278
|
+
|
|
279
|
+
Raises
|
|
280
|
+
------
|
|
281
|
+
ValueError
|
|
282
|
+
If the view_id is not provided.
|
|
283
|
+
"""
|
|
117
284
|
if not view_id:
|
|
118
285
|
error = "Custom View ID undefined."
|
|
119
286
|
raise ValueError(error)
|
|
120
|
-
url = "{
|
|
287
|
+
url = f"{self.baseurl}/{view_id}"
|
|
121
288
|
self.delete_request(url)
|
|
122
|
-
logger.info("Deleted single custom view (ID: {
|
|
289
|
+
logger.info(f"Deleted single custom view (ID: {view_id})")
|
|
123
290
|
|
|
124
291
|
@api(version="3.21")
|
|
125
292
|
def download(self, view_item: CustomViewItem, file: PathOrFileW) -> PathOrFileW:
|
|
293
|
+
"""
|
|
294
|
+
Download the definition of a custom view as json. The file parameter can
|
|
295
|
+
be a file path or a file object. If a file path is provided, the file
|
|
296
|
+
will be written to that location. If a file object is provided, the file
|
|
297
|
+
will be written to that object.
|
|
298
|
+
|
|
299
|
+
May contain sensitive information.
|
|
300
|
+
|
|
301
|
+
Parameters
|
|
302
|
+
----------
|
|
303
|
+
view_item : CustomViewItem
|
|
304
|
+
The custom view item to download.
|
|
305
|
+
|
|
306
|
+
file : PathOrFileW
|
|
307
|
+
The file path or file object to write the custom view to.
|
|
308
|
+
|
|
309
|
+
Returns
|
|
310
|
+
-------
|
|
311
|
+
PathOrFileW
|
|
312
|
+
The file path or file object that the custom view was written to.
|
|
313
|
+
"""
|
|
126
314
|
url = f"{self.expurl}/{view_item.id}/content"
|
|
127
315
|
server_response = self.get_request(url)
|
|
128
316
|
if isinstance(file, io_types_w):
|
|
@@ -136,6 +324,25 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
136
324
|
|
|
137
325
|
@api(version="3.21")
|
|
138
326
|
def publish(self, view_item: CustomViewItem, file: PathOrFileR) -> Optional[CustomViewItem]:
|
|
327
|
+
"""
|
|
328
|
+
Publish a custom view to Tableau Server. The file parameter can be a
|
|
329
|
+
file path or a file object. If a file path is provided, the file will be
|
|
330
|
+
read from that location. If a file object is provided, the file will be
|
|
331
|
+
read from that object.
|
|
332
|
+
|
|
333
|
+
Parameters
|
|
334
|
+
----------
|
|
335
|
+
view_item : CustomViewItem
|
|
336
|
+
The custom view item to publish.
|
|
337
|
+
|
|
338
|
+
file : PathOrFileR
|
|
339
|
+
The file path or file object to read the custom view from.
|
|
340
|
+
|
|
341
|
+
Returns
|
|
342
|
+
-------
|
|
343
|
+
Optional[CustomViewItem]
|
|
344
|
+
The published custom view item.
|
|
345
|
+
"""
|
|
139
346
|
url = self.expurl
|
|
140
347
|
if isinstance(file, io_types_r):
|
|
141
348
|
size = get_file_object_size(file)
|
|
@@ -144,7 +351,7 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
144
351
|
else:
|
|
145
352
|
raise ValueError("File path or file object required for publishing custom view.")
|
|
146
353
|
|
|
147
|
-
if size >= FILESIZE_LIMIT_MB * BYTES_PER_MB:
|
|
354
|
+
if size >= config.FILESIZE_LIMIT_MB * BYTES_PER_MB:
|
|
148
355
|
upload_session_id = self.parent_srv.fileuploads.upload(file)
|
|
149
356
|
url = f"{url}?uploadSessionId={upload_session_id}"
|
|
150
357
|
xml_request, content_type = RequestFactory.CustomView.publish_req_chunked(view_item)
|
|
@@ -163,3 +370,25 @@ class CustomViews(QuerysetEndpoint[CustomViewItem]):
|
|
|
163
370
|
|
|
164
371
|
server_response = self.post_request(url, xml_request, content_type)
|
|
165
372
|
return CustomViewItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
373
|
+
|
|
374
|
+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> "QuerySet[CustomViewItem]":
|
|
375
|
+
"""
|
|
376
|
+
Queries the Tableau Server for items using the specified filters. Page
|
|
377
|
+
size can be specified to limit the number of items returned in a single
|
|
378
|
+
request. If not specified, the default page size is 100. Page size can
|
|
379
|
+
be an integer between 1 and 1000.
|
|
380
|
+
|
|
381
|
+
No positional arguments are allowed. All filters must be specified as
|
|
382
|
+
keyword arguments. If you use the equality operator, you can specify it
|
|
383
|
+
through <field_name>=<value>. If you want to use a different operator,
|
|
384
|
+
you can specify it through <field_name>__<operator>=<value>. Field
|
|
385
|
+
names can either be in snake_case or camelCase.
|
|
386
|
+
|
|
387
|
+
This endpoint supports the following fields and operators:
|
|
388
|
+
|
|
389
|
+
view_id=...
|
|
390
|
+
workbook_id=...
|
|
391
|
+
owner_id=...
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
return super().filter(*invalid, page_size=page_size, **kwargs)
|
|
@@ -10,14 +10,14 @@ from tableauserverclient.helpers.logging import logger
|
|
|
10
10
|
|
|
11
11
|
class DataAccelerationReport(Endpoint):
|
|
12
12
|
def __init__(self, parent_srv):
|
|
13
|
-
super(
|
|
13
|
+
super().__init__(parent_srv)
|
|
14
14
|
|
|
15
15
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
16
16
|
self._default_permissions = _DefaultPermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
17
17
|
|
|
18
18
|
@property
|
|
19
19
|
def baseurl(self):
|
|
20
|
-
return "{
|
|
20
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/dataAccelerationReport"
|
|
21
21
|
|
|
22
22
|
@api(version="3.8")
|
|
23
23
|
def get(self, req_options=None):
|
|
@@ -7,7 +7,7 @@ from tableauserverclient.models import DataAlertItem, PaginationItem, UserItem
|
|
|
7
7
|
|
|
8
8
|
from tableauserverclient.helpers.logging import logger
|
|
9
9
|
|
|
10
|
-
from typing import
|
|
10
|
+
from typing import Optional, TYPE_CHECKING, Union
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
@@ -17,14 +17,14 @@ if TYPE_CHECKING:
|
|
|
17
17
|
|
|
18
18
|
class DataAlerts(Endpoint):
|
|
19
19
|
def __init__(self, parent_srv: "Server") -> None:
|
|
20
|
-
super(
|
|
20
|
+
super().__init__(parent_srv)
|
|
21
21
|
|
|
22
22
|
@property
|
|
23
23
|
def baseurl(self) -> str:
|
|
24
|
-
return "{
|
|
24
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/dataAlerts"
|
|
25
25
|
|
|
26
26
|
@api(version="3.2")
|
|
27
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
27
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[DataAlertItem], PaginationItem]:
|
|
28
28
|
logger.info("Querying all dataAlerts on site")
|
|
29
29
|
url = self.baseurl
|
|
30
30
|
server_response = self.get_request(url, req_options)
|
|
@@ -38,8 +38,8 @@ class DataAlerts(Endpoint):
|
|
|
38
38
|
if not dataAlert_id:
|
|
39
39
|
error = "dataAlert ID undefined."
|
|
40
40
|
raise ValueError(error)
|
|
41
|
-
logger.info("Querying single dataAlert (ID: {
|
|
42
|
-
url = "{
|
|
41
|
+
logger.info(f"Querying single dataAlert (ID: {dataAlert_id})")
|
|
42
|
+
url = f"{self.baseurl}/{dataAlert_id}"
|
|
43
43
|
server_response = self.get_request(url)
|
|
44
44
|
return DataAlertItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
45
45
|
|
|
@@ -55,9 +55,9 @@ class DataAlerts(Endpoint):
|
|
|
55
55
|
error = "Dataalert ID undefined."
|
|
56
56
|
raise ValueError(error)
|
|
57
57
|
# DELETE /api/api-version/sites/site-id/dataAlerts/data-alert-id/users/user-id
|
|
58
|
-
url = "{
|
|
58
|
+
url = f"{self.baseurl}/{dataAlert_id}"
|
|
59
59
|
self.delete_request(url)
|
|
60
|
-
logger.info("Deleted single dataAlert (ID: {
|
|
60
|
+
logger.info(f"Deleted single dataAlert (ID: {dataAlert_id})")
|
|
61
61
|
|
|
62
62
|
@api(version="3.2")
|
|
63
63
|
def delete_user_from_alert(self, dataAlert: Union[DataAlertItem, str], user: Union[UserItem, str]) -> None:
|
|
@@ -80,9 +80,9 @@ class DataAlerts(Endpoint):
|
|
|
80
80
|
error = "User ID undefined."
|
|
81
81
|
raise ValueError(error)
|
|
82
82
|
# DELETE /api/api-version/sites/site-id/dataAlerts/data-alert-id/users/user-id
|
|
83
|
-
url = "{
|
|
83
|
+
url = f"{self.baseurl}/{dataAlert_id}/users/{user_id}"
|
|
84
84
|
self.delete_request(url)
|
|
85
|
-
logger.info("Deleted User (ID {
|
|
85
|
+
logger.info(f"Deleted User (ID {user_id}) from dataAlert (ID: {dataAlert_id})")
|
|
86
86
|
|
|
87
87
|
@api(version="3.2")
|
|
88
88
|
def add_user_to_alert(self, dataAlert_item: DataAlertItem, user: Union[UserItem, str]) -> UserItem:
|
|
@@ -98,10 +98,10 @@ class DataAlerts(Endpoint):
|
|
|
98
98
|
if not user_id:
|
|
99
99
|
error = "User ID undefined."
|
|
100
100
|
raise ValueError(error)
|
|
101
|
-
url = "{
|
|
101
|
+
url = f"{self.baseurl}/{dataAlert_item.id}/users"
|
|
102
102
|
update_req = RequestFactory.DataAlert.add_user_to_alert(dataAlert_item, user_id)
|
|
103
103
|
server_response = self.post_request(url, update_req)
|
|
104
|
-
logger.info("Added user (ID {
|
|
104
|
+
logger.info(f"Added user (ID {user_id}) to dataAlert item (ID: {dataAlert_item.id})")
|
|
105
105
|
added_user = UserItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
106
106
|
return added_user
|
|
107
107
|
|
|
@@ -111,9 +111,9 @@ class DataAlerts(Endpoint):
|
|
|
111
111
|
error = "Dataalert item missing ID."
|
|
112
112
|
raise MissingRequiredFieldError(error)
|
|
113
113
|
|
|
114
|
-
url = "{
|
|
114
|
+
url = f"{self.baseurl}/{dataAlert_item.id}"
|
|
115
115
|
update_req = RequestFactory.DataAlert.update_req(dataAlert_item)
|
|
116
116
|
server_response = self.put_request(url, update_req)
|
|
117
|
-
logger.info("Updated dataAlert item (ID: {
|
|
117
|
+
logger.info(f"Updated dataAlert item (ID: {dataAlert_item.id})")
|
|
118
118
|
updated_dataAlert = DataAlertItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
119
119
|
return updated_dataAlert
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Union
|
|
2
|
+
from typing import Union
|
|
3
|
+
from collections.abc import Iterable
|
|
3
4
|
|
|
4
5
|
from tableauserverclient.server.endpoint.default_permissions_endpoint import _DefaultPermissionsEndpoint
|
|
5
6
|
from tableauserverclient.server.endpoint.dqw_endpoint import _DataQualityWarningEndpoint
|
|
@@ -15,7 +16,7 @@ from tableauserverclient.helpers.logging import logger
|
|
|
15
16
|
|
|
16
17
|
class Databases(Endpoint, TaggingMixin):
|
|
17
18
|
def __init__(self, parent_srv):
|
|
18
|
-
super(
|
|
19
|
+
super().__init__(parent_srv)
|
|
19
20
|
|
|
20
21
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
21
22
|
self._default_permissions = _DefaultPermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
@@ -23,7 +24,7 @@ class Databases(Endpoint, TaggingMixin):
|
|
|
23
24
|
|
|
24
25
|
@property
|
|
25
26
|
def baseurl(self):
|
|
26
|
-
return "{
|
|
27
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/databases"
|
|
27
28
|
|
|
28
29
|
@api(version="3.5")
|
|
29
30
|
def get(self, req_options=None):
|
|
@@ -40,8 +41,8 @@ class Databases(Endpoint, TaggingMixin):
|
|
|
40
41
|
if not database_id:
|
|
41
42
|
error = "database ID undefined."
|
|
42
43
|
raise ValueError(error)
|
|
43
|
-
logger.info("Querying single database (ID: {
|
|
44
|
-
url = "{
|
|
44
|
+
logger.info(f"Querying single database (ID: {database_id})")
|
|
45
|
+
url = f"{self.baseurl}/{database_id}"
|
|
45
46
|
server_response = self.get_request(url)
|
|
46
47
|
return DatabaseItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
47
48
|
|
|
@@ -50,9 +51,9 @@ class Databases(Endpoint, TaggingMixin):
|
|
|
50
51
|
if not database_id:
|
|
51
52
|
error = "Database ID undefined."
|
|
52
53
|
raise ValueError(error)
|
|
53
|
-
url = "{
|
|
54
|
+
url = f"{self.baseurl}/{database_id}"
|
|
54
55
|
self.delete_request(url)
|
|
55
|
-
logger.info("Deleted single database (ID: {
|
|
56
|
+
logger.info(f"Deleted single database (ID: {database_id})")
|
|
56
57
|
|
|
57
58
|
@api(version="3.5")
|
|
58
59
|
def update(self, database_item):
|
|
@@ -60,10 +61,10 @@ class Databases(Endpoint, TaggingMixin):
|
|
|
60
61
|
error = "Database item missing ID."
|
|
61
62
|
raise MissingRequiredFieldError(error)
|
|
62
63
|
|
|
63
|
-
url = "{
|
|
64
|
+
url = f"{self.baseurl}/{database_item.id}"
|
|
64
65
|
update_req = RequestFactory.Database.update_req(database_item)
|
|
65
66
|
server_response = self.put_request(url, update_req)
|
|
66
|
-
logger.info("Updated database item (ID: {
|
|
67
|
+
logger.info(f"Updated database item (ID: {database_item.id})")
|
|
67
68
|
updated_database = DatabaseItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
68
69
|
return updated_database
|
|
69
70
|
|
|
@@ -78,10 +79,10 @@ class Databases(Endpoint, TaggingMixin):
|
|
|
78
79
|
return self._get_tables_for_database(database_item)
|
|
79
80
|
|
|
80
81
|
database_item._set_tables(column_fetcher)
|
|
81
|
-
logger.info("Populated tables for database (ID: {
|
|
82
|
+
logger.info(f"Populated tables for database (ID: {database_item.id}")
|
|
82
83
|
|
|
83
84
|
def _get_tables_for_database(self, database_item):
|
|
84
|
-
url = "{
|
|
85
|
+
url = f"{self.baseurl}/{database_item.id}/tables"
|
|
85
86
|
server_response = self.get_request(url)
|
|
86
87
|
tables = TableItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
87
88
|
return tables
|
|
@@ -127,7 +128,7 @@ class Databases(Endpoint, TaggingMixin):
|
|
|
127
128
|
self._data_quality_warnings.clear(item)
|
|
128
129
|
|
|
129
130
|
@api(version="3.9")
|
|
130
|
-
def add_tags(self, item: Union[DatabaseItem, str], tags: Iterable[str]) ->
|
|
131
|
+
def add_tags(self, item: Union[DatabaseItem, str], tags: Iterable[str]) -> set[str]:
|
|
131
132
|
return super().add_tags(item, tags)
|
|
132
133
|
|
|
133
134
|
@api(version="3.9")
|