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.
Files changed (94) hide show
  1. tableauserverclient/__init__.py +33 -23
  2. tableauserverclient/{_version.py → bin/_version.py} +3 -3
  3. tableauserverclient/config.py +5 -3
  4. tableauserverclient/models/column_item.py +1 -1
  5. tableauserverclient/models/connection_credentials.py +18 -2
  6. tableauserverclient/models/connection_item.py +44 -6
  7. tableauserverclient/models/custom_view_item.py +78 -11
  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 +54 -9
  17. tableauserverclient/models/flow_run_item.py +3 -3
  18. tableauserverclient/models/group_item.py +44 -4
  19. tableauserverclient/models/groupset_item.py +4 -4
  20. tableauserverclient/models/interval_item.py +9 -9
  21. tableauserverclient/models/job_item.py +73 -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 +73 -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 +34 -4
  39. tableauserverclient/models/user_item.py +47 -17
  40. tableauserverclient/models/view_item.py +66 -13
  41. tableauserverclient/models/virtual_connection_item.py +6 -5
  42. tableauserverclient/models/webhook_item.py +39 -6
  43. tableauserverclient/models/workbook_item.py +116 -13
  44. tableauserverclient/namespace.py +1 -1
  45. tableauserverclient/server/__init__.py +2 -1
  46. tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
  47. tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
  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 +61 -62
  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 +344 -29
  61. tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
  62. tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
  63. tableauserverclient/server/endpoint/jobs_endpoint.py +116 -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 +681 -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 +86 -8
  76. tableauserverclient/server/endpoint/users_endpoint.py +366 -19
  77. tableauserverclient/server/endpoint/views_endpoint.py +262 -20
  78. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
  79. tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
  80. tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
  81. tableauserverclient/server/filter.py +2 -2
  82. tableauserverclient/server/pager.py +29 -6
  83. tableauserverclient/server/query.py +68 -19
  84. tableauserverclient/server/request_factory.py +57 -37
  85. tableauserverclient/server/request_options.py +243 -141
  86. tableauserverclient/server/server.py +76 -10
  87. tableauserverclient/server/sort.py +16 -2
  88. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
  89. tableauserverclient-0.35.dist-info/RECORD +106 -0
  90. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
  91. tableauserverclient-0.33.dist-info/RECORD +0 -106
  92. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
  93. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
  94. {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 List, Optional, Tuple, Union
6
+ from typing import Optional, Union, TYPE_CHECKING
7
+ from collections.abc import Iterator
6
8
 
7
- from tableauserverclient.config import BYTES_PER_MB, FILESIZE_LIMIT_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 RequestFactory, RequestOptions, ImageRequestOptions
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(CustomViews, self).__init__(parent_srv)
47
+ super().__init__(parent_srv)
37
48
 
38
49
  @property
39
50
  def baseurl(self) -> str:
40
- return "{0}/sites/{1}/customviews".format(self.parent_srv.baseurl, self.parent_srv.site_id)
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) -> Tuple[List[CustomViewItem], PaginationItem]:
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: {0})".format(view_id))
72
- url = "{0}/{1}".format(self.baseurl, view_id)
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: {0})".format(view_item.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 = "{0}/{1}/image".format(self.baseurl, view_item.id)
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
- Not yet implemented: pdf or csv exports
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 = "{0}/{1}".format(self.baseurl, view_item.id)
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: {0})".format(view_item.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 = "{0}/{1}".format(self.baseurl, view_id)
287
+ url = f"{self.baseurl}/{view_id}"
121
288
  self.delete_request(url)
122
- logger.info("Deleted single custom view (ID: {0})".format(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(DataAccelerationReport, self).__init__(parent_srv)
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 "{0}/sites/{1}/dataAccelerationReport".format(self.parent_srv.baseurl, self.parent_srv.site_id)
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 List, Optional, TYPE_CHECKING, Tuple, Union
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(DataAlerts, self).__init__(parent_srv)
20
+ super().__init__(parent_srv)
21
21
 
22
22
  @property
23
23
  def baseurl(self) -> str:
24
- return "{0}/sites/{1}/dataAlerts".format(self.parent_srv.baseurl, self.parent_srv.site_id)
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) -> Tuple[List[DataAlertItem], PaginationItem]:
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: {0})".format(dataAlert_id))
42
- url = "{0}/{1}".format(self.baseurl, dataAlert_id)
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 = "{0}/{1}".format(self.baseurl, dataAlert_id)
58
+ url = f"{self.baseurl}/{dataAlert_id}"
59
59
  self.delete_request(url)
60
- logger.info("Deleted single dataAlert (ID: {0})".format(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 = "{0}/{1}/users/{2}".format(self.baseurl, dataAlert_id, user_id)
83
+ url = f"{self.baseurl}/{dataAlert_id}/users/{user_id}"
84
84
  self.delete_request(url)
85
- logger.info("Deleted User (ID {0}) from dataAlert (ID: {1})".format(user_id, dataAlert_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 = "{0}/{1}/users".format(self.baseurl, dataAlert_item.id)
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 {0}) to dataAlert item (ID: {1})".format(user_id, dataAlert_item.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 = "{0}/{1}".format(self.baseurl, dataAlert_item.id)
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: {0})".format(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, Iterable, Set
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(Databases, self).__init__(parent_srv)
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 "{0}/sites/{1}/databases".format(self.parent_srv.baseurl, self.parent_srv.site_id)
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: {0})".format(database_id))
44
- url = "{0}/{1}".format(self.baseurl, database_id)
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 = "{0}/{1}".format(self.baseurl, database_id)
54
+ url = f"{self.baseurl}/{database_id}"
54
55
  self.delete_request(url)
55
- logger.info("Deleted single database (ID: {0})".format(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 = "{0}/{1}".format(self.baseurl, database_item.id)
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: {0})".format(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: {0}".format(database_item.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 = "{0}/{1}/tables".format(self.baseurl, database_item.id)
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]) -> Set[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")