tableauserverclient 0.32__py3-none-any.whl → 0.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tableauserverclient/__init__.py +34 -18
- tableauserverclient/_version.py +3 -3
- tableauserverclient/config.py +20 -6
- tableauserverclient/models/__init__.py +12 -0
- tableauserverclient/models/column_item.py +1 -1
- tableauserverclient/models/connection_credentials.py +1 -1
- tableauserverclient/models/connection_item.py +10 -8
- tableauserverclient/models/custom_view_item.py +29 -6
- tableauserverclient/models/data_acceleration_report_item.py +2 -2
- tableauserverclient/models/data_alert_item.py +5 -5
- tableauserverclient/models/data_freshness_policy_item.py +6 -6
- tableauserverclient/models/database_item.py +8 -2
- 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 +12 -12
- tableauserverclient/models/flow_run_item.py +3 -3
- tableauserverclient/models/group_item.py +4 -4
- tableauserverclient/models/groupset_item.py +53 -0
- tableauserverclient/models/interval_item.py +36 -23
- tableauserverclient/models/job_item.py +26 -10
- tableauserverclient/models/linked_tasks_item.py +102 -0
- tableauserverclient/models/metric_item.py +5 -5
- tableauserverclient/models/pagination_item.py +1 -1
- tableauserverclient/models/permissions_item.py +19 -14
- tableauserverclient/models/project_item.py +35 -19
- tableauserverclient/models/property_decorators.py +12 -11
- tableauserverclient/models/reference_item.py +2 -2
- tableauserverclient/models/revision_item.py +3 -3
- tableauserverclient/models/schedule_item.py +2 -2
- tableauserverclient/models/server_info_item.py +26 -6
- tableauserverclient/models/site_item.py +69 -3
- tableauserverclient/models/subscription_item.py +3 -3
- tableauserverclient/models/table_item.py +1 -1
- tableauserverclient/models/tableau_auth.py +115 -5
- tableauserverclient/models/tableau_types.py +11 -9
- tableauserverclient/models/tag_item.py +3 -4
- tableauserverclient/models/task_item.py +4 -4
- tableauserverclient/models/user_item.py +47 -17
- tableauserverclient/models/view_item.py +11 -10
- tableauserverclient/models/virtual_connection_item.py +78 -0
- tableauserverclient/models/webhook_item.py +6 -6
- tableauserverclient/models/workbook_item.py +90 -12
- tableauserverclient/namespace.py +1 -1
- tableauserverclient/server/__init__.py +2 -1
- tableauserverclient/server/endpoint/__init__.py +8 -0
- tableauserverclient/server/endpoint/auth_endpoint.py +68 -11
- tableauserverclient/server/endpoint/custom_views_endpoint.py +124 -19
- 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 +32 -17
- tableauserverclient/server/endpoint/datasources_endpoint.py +150 -59
- tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
- tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
- tableauserverclient/server/endpoint/endpoint.py +47 -31
- tableauserverclient/server/endpoint/exceptions.py +23 -7
- tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
- tableauserverclient/server/endpoint/fileuploads_endpoint.py +11 -13
- tableauserverclient/server/endpoint/flow_runs_endpoint.py +59 -17
- tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
- tableauserverclient/server/endpoint/flows_endpoint.py +73 -35
- tableauserverclient/server/endpoint/groups_endpoint.py +96 -27
- tableauserverclient/server/endpoint/groupsets_endpoint.py +127 -0
- tableauserverclient/server/endpoint/jobs_endpoint.py +79 -12
- tableauserverclient/server/endpoint/linked_tasks_endpoint.py +45 -0
- 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 +124 -30
- tableauserverclient/server/endpoint/resource_tagger.py +139 -6
- 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 +33 -19
- tableauserverclient/server/endpoint/tasks_endpoint.py +8 -8
- tableauserverclient/server/endpoint/users_endpoint.py +405 -19
- tableauserverclient/server/endpoint/views_endpoint.py +111 -25
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +174 -0
- tableauserverclient/server/endpoint/webhooks_endpoint.py +11 -11
- tableauserverclient/server/endpoint/workbooks_endpoint.py +735 -68
- tableauserverclient/server/filter.py +2 -2
- tableauserverclient/server/pager.py +8 -10
- tableauserverclient/server/query.py +70 -20
- tableauserverclient/server/request_factory.py +213 -41
- tableauserverclient/server/request_options.py +125 -145
- tableauserverclient/server/server.py +73 -9
- tableauserverclient/server/sort.py +2 -2
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/METADATA +17 -17
- tableauserverclient-0.34.dist-info/RECORD +106 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/WHEEL +1 -1
- tableauserverclient-0.32.dist-info/RECORD +0 -100
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.32.dist-info → tableauserverclient-0.34.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from .request_options import RequestOptions
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class Filter
|
|
4
|
+
class Filter:
|
|
5
5
|
def __init__(self, field, operator, value):
|
|
6
6
|
self.field = field
|
|
7
7
|
self.operator = operator
|
|
@@ -16,7 +16,7 @@ class Filter(object):
|
|
|
16
16
|
# to [<string1>,<string2>]
|
|
17
17
|
# so effectively, remove any spaces between "," and "'" and then remove all "'"
|
|
18
18
|
value_string = value_string.replace(", '", ",'").replace("'", "")
|
|
19
|
-
return "{
|
|
19
|
+
return f"{self.field}:{self.operator}:{value_string}"
|
|
20
20
|
|
|
21
21
|
@property
|
|
22
22
|
def value(self):
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
from functools import partial
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional, Protocol, TypeVar, Union, runtime_checkable
|
|
4
|
+
from collections.abc import Iterable, Iterator
|
|
4
5
|
|
|
5
6
|
from tableauserverclient.models.pagination_item import PaginationItem
|
|
6
7
|
from tableauserverclient.server.request_options import RequestOptions
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
T = TypeVar("T")
|
|
10
|
-
ReturnType = Tuple[List[T], PaginationItem]
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@runtime_checkable
|
|
14
|
-
class Endpoint(Protocol):
|
|
15
|
-
def get(self, req_options: Optional[RequestOptions]
|
|
16
|
-
...
|
|
14
|
+
class Endpoint(Protocol[T]):
|
|
15
|
+
def get(self, req_options: Optional[RequestOptions]) -> tuple[list[T], PaginationItem]: ...
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
@runtime_checkable
|
|
20
|
-
class CallableEndpoint(Protocol):
|
|
21
|
-
def __call__(self, __req_options: Optional[RequestOptions], **kwargs) ->
|
|
22
|
-
...
|
|
19
|
+
class CallableEndpoint(Protocol[T]):
|
|
20
|
+
def __call__(self, __req_options: Optional[RequestOptions], **kwargs) -> tuple[list[T], PaginationItem]: ...
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
class Pager(Iterable[T]):
|
|
@@ -28,12 +26,12 @@ class Pager(Iterable[T]):
|
|
|
28
26
|
Supports all `RequestOptions` including starting on any page. Also used by models to load sub-models
|
|
29
27
|
(users in a group, views in a workbook, etc) by passing a different endpoint.
|
|
30
28
|
|
|
31
|
-
Will loop over anything that returns (
|
|
29
|
+
Will loop over anything that returns (list[ModelItem], PaginationItem).
|
|
32
30
|
"""
|
|
33
31
|
|
|
34
32
|
def __init__(
|
|
35
33
|
self,
|
|
36
|
-
endpoint: Union[CallableEndpoint, Endpoint],
|
|
34
|
+
endpoint: Union[CallableEndpoint[T], Endpoint[T]],
|
|
37
35
|
request_opts: Optional[RequestOptions] = None,
|
|
38
36
|
**kwargs,
|
|
39
37
|
) -> None:
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
from collections.abc import Sized
|
|
1
|
+
from collections.abc import Iterable, Iterator, Sized
|
|
2
2
|
from itertools import count
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional, Protocol, TYPE_CHECKING, TypeVar, overload
|
|
4
|
+
import sys
|
|
5
|
+
from tableauserverclient.config import config
|
|
4
6
|
from tableauserverclient.models.pagination_item import PaginationItem
|
|
7
|
+
from tableauserverclient.server.endpoint.exceptions import ServerResponseError
|
|
5
8
|
from tableauserverclient.server.filter import Filter
|
|
6
9
|
from tableauserverclient.server.request_options import RequestOptions
|
|
7
10
|
from tableauserverclient.server.sort import Sort
|
|
@@ -33,10 +36,36 @@ see pagination_sample
|
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
class QuerySet(Iterable[T], Sized):
|
|
39
|
+
"""
|
|
40
|
+
QuerySet is a class that allows easy filtering, sorting, and iterating over
|
|
41
|
+
many endpoints in TableauServerClient. It is designed to be used in a similar
|
|
42
|
+
way to Django QuerySets, but with a more limited feature set.
|
|
43
|
+
|
|
44
|
+
QuerySet is an iterable, and can be used in for loops, list comprehensions,
|
|
45
|
+
and other places where iterables are expected.
|
|
46
|
+
|
|
47
|
+
QuerySet is also Sized, and can be used in places where the length of the
|
|
48
|
+
QuerySet is needed. The length of the QuerySet is the total number of items
|
|
49
|
+
available in the QuerySet, not just the number of items that have been
|
|
50
|
+
fetched. If the endpoint does not return a total count of items, the length
|
|
51
|
+
of the QuerySet will be sys.maxsize. If there is no total count, the
|
|
52
|
+
QuerySet will continue to fetch items until there are no more items to
|
|
53
|
+
fetch.
|
|
54
|
+
|
|
55
|
+
QuerySet is not re-entrant. It is not designed to be used in multiple places
|
|
56
|
+
at the same time. If you need to use a QuerySet in multiple places, you
|
|
57
|
+
should create a new QuerySet for each place you need to use it, convert it
|
|
58
|
+
to a list, or create a deep copy of the QuerySet.
|
|
59
|
+
|
|
60
|
+
QuerySets are also indexable, and can be sliced. If you try to access an
|
|
61
|
+
index that has not been fetched, the QuerySet will fetch the page that
|
|
62
|
+
contains the item you are looking for.
|
|
63
|
+
"""
|
|
64
|
+
|
|
36
65
|
def __init__(self, model: "QuerysetEndpoint[T]", page_size: Optional[int] = None) -> None:
|
|
37
66
|
self.model = model
|
|
38
|
-
self.request_options = RequestOptions(pagesize=page_size or
|
|
39
|
-
self._result_cache:
|
|
67
|
+
self.request_options = RequestOptions(pagesize=page_size or config.PAGE_SIZE)
|
|
68
|
+
self._result_cache: list[T] = []
|
|
40
69
|
self._pagination_item = PaginationItem()
|
|
41
70
|
|
|
42
71
|
def __iter__(self: Self) -> Iterator[T]:
|
|
@@ -48,19 +77,30 @@ class QuerySet(Iterable[T], Sized):
|
|
|
48
77
|
for page in count(1):
|
|
49
78
|
self.request_options.pagenumber = page
|
|
50
79
|
self._result_cache = []
|
|
51
|
-
self.
|
|
80
|
+
self._pagination_item._page_number = None
|
|
81
|
+
try:
|
|
82
|
+
self._fetch_all()
|
|
83
|
+
except ServerResponseError as e:
|
|
84
|
+
if e.code == "400006":
|
|
85
|
+
# If the endpoint does not support pagination, it will end
|
|
86
|
+
# up overrunning the total number of pages. Catch the
|
|
87
|
+
# error and break out of the loop.
|
|
88
|
+
raise StopIteration
|
|
89
|
+
if len(self._result_cache) == 0:
|
|
90
|
+
return
|
|
52
91
|
yield from self._result_cache
|
|
53
|
-
#
|
|
54
|
-
|
|
92
|
+
# If the length of the QuerySet is unknown, continue fetching until
|
|
93
|
+
# the result cache is empty.
|
|
94
|
+
if (size := len(self)) == 0:
|
|
95
|
+
continue
|
|
96
|
+
if (page * self.page_size) >= size:
|
|
55
97
|
return
|
|
56
98
|
|
|
57
99
|
@overload
|
|
58
|
-
def __getitem__(self: Self, k: Slice) ->
|
|
59
|
-
...
|
|
100
|
+
def __getitem__(self: Self, k: Slice) -> list[T]: ...
|
|
60
101
|
|
|
61
102
|
@overload
|
|
62
|
-
def __getitem__(self: Self, k: int) -> T:
|
|
63
|
-
...
|
|
103
|
+
def __getitem__(self: Self, k: int) -> T: ...
|
|
64
104
|
|
|
65
105
|
def __getitem__(self, k):
|
|
66
106
|
page = self.page_number
|
|
@@ -102,6 +142,7 @@ class QuerySet(Iterable[T], Sized):
|
|
|
102
142
|
elif k in range(self.total_available):
|
|
103
143
|
# Otherwise, check if k is even sensible to return
|
|
104
144
|
self._result_cache = []
|
|
145
|
+
self._pagination_item._page_number = None
|
|
105
146
|
# Add one to k, otherwise it gets stuck at page boundaries, e.g. 100
|
|
106
147
|
self.request_options.pagenumber = max(1, math.ceil((k + 1) / size))
|
|
107
148
|
return self[k]
|
|
@@ -113,11 +154,16 @@ class QuerySet(Iterable[T], Sized):
|
|
|
113
154
|
"""
|
|
114
155
|
Retrieve the data and store result and pagination item in cache
|
|
115
156
|
"""
|
|
116
|
-
if not self._result_cache:
|
|
117
|
-
|
|
157
|
+
if not self._result_cache and self._pagination_item._page_number is None:
|
|
158
|
+
response = self.model.get(self.request_options)
|
|
159
|
+
if isinstance(response, tuple):
|
|
160
|
+
self._result_cache, self._pagination_item = response
|
|
161
|
+
else:
|
|
162
|
+
self._result_cache = response
|
|
163
|
+
self._pagination_item = PaginationItem()
|
|
118
164
|
|
|
119
165
|
def __len__(self: Self) -> int:
|
|
120
|
-
return self.total_available
|
|
166
|
+
return sys.maxsize if self.total_available is None else self.total_available
|
|
121
167
|
|
|
122
168
|
@property
|
|
123
169
|
def total_available(self: Self) -> int:
|
|
@@ -127,12 +173,16 @@ class QuerySet(Iterable[T], Sized):
|
|
|
127
173
|
@property
|
|
128
174
|
def page_number(self: Self) -> int:
|
|
129
175
|
self._fetch_all()
|
|
130
|
-
|
|
176
|
+
# If the PaginationItem is not returned from the endpoint, use the
|
|
177
|
+
# pagenumber from the RequestOptions.
|
|
178
|
+
return self._pagination_item.page_number or self.request_options.pagenumber
|
|
131
179
|
|
|
132
180
|
@property
|
|
133
181
|
def page_size(self: Self) -> int:
|
|
134
182
|
self._fetch_all()
|
|
135
|
-
|
|
183
|
+
# If the PaginationItem is not returned from the endpoint, use the
|
|
184
|
+
# pagesize from the RequestOptions.
|
|
185
|
+
return self._pagination_item.page_size or self.request_options.pagesize
|
|
136
186
|
|
|
137
187
|
def filter(self: Self, *invalid, page_size: Optional[int] = None, **kwargs) -> Self:
|
|
138
188
|
if invalid:
|
|
@@ -159,22 +209,22 @@ class QuerySet(Iterable[T], Sized):
|
|
|
159
209
|
return self
|
|
160
210
|
|
|
161
211
|
@staticmethod
|
|
162
|
-
def _parse_shorthand_filter(key: str) ->
|
|
212
|
+
def _parse_shorthand_filter(key: str) -> tuple[str, str]:
|
|
163
213
|
tokens = key.split("__", 1)
|
|
164
214
|
if len(tokens) == 1:
|
|
165
215
|
operator = RequestOptions.Operator.Equals
|
|
166
216
|
else:
|
|
167
217
|
operator = tokens[1]
|
|
168
218
|
if operator not in RequestOptions.Operator.__dict__.values():
|
|
169
|
-
raise ValueError("Operator `{}` is not valid."
|
|
219
|
+
raise ValueError(f"Operator `{operator}` is not valid.")
|
|
170
220
|
|
|
171
221
|
field = to_camel_case(tokens[0])
|
|
172
222
|
if field not in RequestOptions.Field.__dict__.values():
|
|
173
|
-
raise ValueError("Field name `{}` is not valid."
|
|
223
|
+
raise ValueError(f"Field name `{field}` is not valid.")
|
|
174
224
|
return (field, operator)
|
|
175
225
|
|
|
176
226
|
@staticmethod
|
|
177
|
-
def _parse_shorthand_sort(key: str) ->
|
|
227
|
+
def _parse_shorthand_sort(key: str) -> tuple[str, str]:
|
|
178
228
|
direction = RequestOptions.Direction.Asc
|
|
179
229
|
if key.startswith("-"):
|
|
180
230
|
direction = RequestOptions.Direction.Desc
|