tableauserverclient 0.37__py3-none-any.whl → 0.38__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 +4 -0
- tableauserverclient/bin/_version.py +3 -3
- tableauserverclient/helpers/strings.py +25 -1
- tableauserverclient/models/__init__.py +6 -1
- tableauserverclient/models/datasource_item.py +110 -2
- tableauserverclient/models/extract_item.py +82 -0
- tableauserverclient/models/flow_item.py +2 -2
- tableauserverclient/models/group_item.py +11 -0
- tableauserverclient/models/interval_item.py +40 -0
- tableauserverclient/models/location_item.py +53 -0
- tableauserverclient/models/project_item.py +138 -27
- tableauserverclient/models/schedule_item.py +57 -0
- tableauserverclient/models/site_item.py +28 -0
- tableauserverclient/models/table_item.py +7 -3
- tableauserverclient/models/tableau_types.py +13 -1
- tableauserverclient/models/user_item.py +101 -1
- tableauserverclient/models/view_item.py +79 -5
- tableauserverclient/models/workbook_item.py +151 -1
- tableauserverclient/server/endpoint/databases_endpoint.py +101 -18
- tableauserverclient/server/endpoint/datasources_endpoint.py +3 -3
- tableauserverclient/server/endpoint/dqw_endpoint.py +16 -6
- tableauserverclient/server/endpoint/endpoint.py +39 -0
- tableauserverclient/server/endpoint/schedules_endpoint.py +132 -2
- tableauserverclient/server/endpoint/sites_endpoint.py +18 -1
- tableauserverclient/server/endpoint/tables_endpoint.py +140 -17
- tableauserverclient/server/endpoint/users_endpoint.py +22 -5
- tableauserverclient/server/query.py +36 -0
- tableauserverclient/server/request_factory.py +5 -0
- tableauserverclient/server/request_options.py +128 -2
- tableauserverclient/server/server.py +42 -0
- {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/METADATA +1 -1
- {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/RECORD +36 -34
- {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/WHEEL +1 -1
- {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/licenses/LICENSE +0 -0
- {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/licenses/LICENSE.versioneer +0 -0
- {tableauserverclient-0.37.dist-info → tableauserverclient-0.38.dist-info}/top_level.txt +0 -0
tableauserverclient/__init__.py
CHANGED
|
@@ -25,6 +25,7 @@ from tableauserverclient.models import (
|
|
|
25
25
|
LinkedTaskItem,
|
|
26
26
|
LinkedTaskStepItem,
|
|
27
27
|
LinkedTaskFlowRunItem,
|
|
28
|
+
LocationItem,
|
|
28
29
|
MetricItem,
|
|
29
30
|
MonthlyInterval,
|
|
30
31
|
PaginationItem,
|
|
@@ -35,6 +36,7 @@ from tableauserverclient.models import (
|
|
|
35
36
|
Resource,
|
|
36
37
|
RevisionItem,
|
|
37
38
|
ScheduleItem,
|
|
39
|
+
SiteAuthConfiguration,
|
|
38
40
|
SiteItem,
|
|
39
41
|
ServerInfoItem,
|
|
40
42
|
SubscriptionItem,
|
|
@@ -101,6 +103,7 @@ __all__ = [
|
|
|
101
103
|
"LinkedTaskFlowRunItem",
|
|
102
104
|
"LinkedTaskItem",
|
|
103
105
|
"LinkedTaskStepItem",
|
|
106
|
+
"LocationItem",
|
|
104
107
|
"MetricItem",
|
|
105
108
|
"MissingRequiredFieldError",
|
|
106
109
|
"MonthlyInterval",
|
|
@@ -121,6 +124,7 @@ __all__ = [
|
|
|
121
124
|
"ServerInfoItem",
|
|
122
125
|
"ServerResponseError",
|
|
123
126
|
"SiteItem",
|
|
127
|
+
"SiteAuthConfiguration",
|
|
124
128
|
"Sort",
|
|
125
129
|
"SubscriptionItem",
|
|
126
130
|
"TableauAuth",
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-05-15T14:21:50-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.
|
|
14
|
+
"full-revisionid": "cda018b684c98200f3f9de11e379ba2ca3b1b3fe",
|
|
15
|
+
"version": "0.38"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from defusedxml.ElementTree import fromstring, tostring
|
|
2
2
|
from functools import singledispatch
|
|
3
|
-
from typing import TypeVar
|
|
3
|
+
from typing import TypeVar, overload
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
# the redact method can handle either strings or bytes, but it can't mix them.
|
|
@@ -41,3 +41,27 @@ def _(xml: str) -> str:
|
|
|
41
41
|
@redact_xml.register # type: ignore[no-redef]
|
|
42
42
|
def _(xml: bytes) -> bytes:
|
|
43
43
|
return _redact_any_type(bytearray(xml), b"password", b"..[redacted]")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@overload
|
|
47
|
+
def nullable_str_to_int(value: None) -> None: ...
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@overload
|
|
51
|
+
def nullable_str_to_int(value: str) -> int: ...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def nullable_str_to_int(value):
|
|
55
|
+
return int(value) if value is not None else None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@overload
|
|
59
|
+
def nullable_str_to_bool(value: None) -> None: ...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@overload
|
|
63
|
+
def nullable_str_to_bool(value: str) -> bool: ...
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def nullable_str_to_bool(value):
|
|
67
|
+
return str(value).lower() == "true" if value is not None else None
|
|
@@ -28,6 +28,7 @@ from tableauserverclient.models.linked_tasks_item import (
|
|
|
28
28
|
LinkedTaskStepItem,
|
|
29
29
|
LinkedTaskFlowRunItem,
|
|
30
30
|
)
|
|
31
|
+
from tableauserverclient.models.location_item import LocationItem
|
|
31
32
|
from tableauserverclient.models.metric_item import MetricItem
|
|
32
33
|
from tableauserverclient.models.pagination_item import PaginationItem
|
|
33
34
|
from tableauserverclient.models.permissions_item import PermissionsRule, Permission
|
|
@@ -35,7 +36,7 @@ from tableauserverclient.models.project_item import ProjectItem
|
|
|
35
36
|
from tableauserverclient.models.revision_item import RevisionItem
|
|
36
37
|
from tableauserverclient.models.schedule_item import ScheduleItem
|
|
37
38
|
from tableauserverclient.models.server_info_item import ServerInfoItem
|
|
38
|
-
from tableauserverclient.models.site_item import SiteItem
|
|
39
|
+
from tableauserverclient.models.site_item import SiteItem, SiteAuthConfiguration
|
|
39
40
|
from tableauserverclient.models.subscription_item import SubscriptionItem
|
|
40
41
|
from tableauserverclient.models.table_item import TableItem
|
|
41
42
|
from tableauserverclient.models.tableau_auth import Credentials, TableauAuth, PersonalAccessTokenAuth, JWTAuth
|
|
@@ -48,6 +49,7 @@ from tableauserverclient.models.view_item import ViewItem
|
|
|
48
49
|
from tableauserverclient.models.virtual_connection_item import VirtualConnectionItem
|
|
49
50
|
from tableauserverclient.models.webhook_item import WebhookItem
|
|
50
51
|
from tableauserverclient.models.workbook_item import WorkbookItem
|
|
52
|
+
from tableauserverclient.models.extract_item import ExtractItem
|
|
51
53
|
|
|
52
54
|
__all__ = [
|
|
53
55
|
"ColumnItem",
|
|
@@ -75,6 +77,7 @@ __all__ = [
|
|
|
75
77
|
"MonthlyInterval",
|
|
76
78
|
"HourlyInterval",
|
|
77
79
|
"BackgroundJobItem",
|
|
80
|
+
"LocationItem",
|
|
78
81
|
"MetricItem",
|
|
79
82
|
"PaginationItem",
|
|
80
83
|
"Permission",
|
|
@@ -83,6 +86,7 @@ __all__ = [
|
|
|
83
86
|
"RevisionItem",
|
|
84
87
|
"ScheduleItem",
|
|
85
88
|
"ServerInfoItem",
|
|
89
|
+
"SiteAuthConfiguration",
|
|
86
90
|
"SiteItem",
|
|
87
91
|
"SubscriptionItem",
|
|
88
92
|
"TableItem",
|
|
@@ -103,4 +107,5 @@ __all__ = [
|
|
|
103
107
|
"LinkedTaskItem",
|
|
104
108
|
"LinkedTaskStepItem",
|
|
105
109
|
"LinkedTaskFlowRunItem",
|
|
110
|
+
"ExtractItem",
|
|
106
111
|
]
|
|
@@ -6,9 +6,11 @@ from typing import Optional
|
|
|
6
6
|
from defusedxml.ElementTree import fromstring
|
|
7
7
|
|
|
8
8
|
from tableauserverclient.datetime_helpers import parse_datetime
|
|
9
|
+
from tableauserverclient.helpers.strings import nullable_str_to_bool, nullable_str_to_int
|
|
9
10
|
from tableauserverclient.models.connection_item import ConnectionItem
|
|
10
11
|
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
|
|
11
12
|
from tableauserverclient.models.permissions_item import PermissionsRule
|
|
13
|
+
from tableauserverclient.models.project_item import ProjectItem
|
|
12
14
|
from tableauserverclient.models.property_decorators import (
|
|
13
15
|
property_not_nullable,
|
|
14
16
|
property_is_boolean,
|
|
@@ -16,6 +18,7 @@ from tableauserverclient.models.property_decorators import (
|
|
|
16
18
|
)
|
|
17
19
|
from tableauserverclient.models.revision_item import RevisionItem
|
|
18
20
|
from tableauserverclient.models.tag_item import TagItem
|
|
21
|
+
from tableauserverclient.models.user_item import UserItem
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
class DatasourceItem:
|
|
@@ -40,6 +43,9 @@ class DatasourceItem:
|
|
|
40
43
|
specified, it will default to SiteDefault. See REST API Publish
|
|
41
44
|
Datasource for more information about ask_data_enablement.
|
|
42
45
|
|
|
46
|
+
connected_workbooks_count : Optional[int]
|
|
47
|
+
The number of workbooks connected to the datasource.
|
|
48
|
+
|
|
43
49
|
connections : list[ConnectionItem]
|
|
44
50
|
The list of data connections (ConnectionItem) for the specified data
|
|
45
51
|
source. You must first call the populate_connections method to access
|
|
@@ -67,6 +73,12 @@ class DatasourceItem:
|
|
|
67
73
|
A Boolean value to determine if a datasource should be encrypted or not.
|
|
68
74
|
See Extract and Encryption Methods for more information.
|
|
69
75
|
|
|
76
|
+
favorites_total : Optional[int]
|
|
77
|
+
The number of users who have marked the data source as a favorite.
|
|
78
|
+
|
|
79
|
+
has_alert : Optional[bool]
|
|
80
|
+
A Boolean value that indicates whether the data source has an alert.
|
|
81
|
+
|
|
70
82
|
has_extracts : Optional[bool]
|
|
71
83
|
A Boolean value that indicates whether the datasource has extracts.
|
|
72
84
|
|
|
@@ -75,13 +87,22 @@ class DatasourceItem:
|
|
|
75
87
|
specific data source or to delete a data source with the get_by_id and
|
|
76
88
|
delete methods.
|
|
77
89
|
|
|
90
|
+
is_published : Optional[bool]
|
|
91
|
+
A Boolean value that indicates whether the data source is published.
|
|
92
|
+
|
|
78
93
|
name : Optional[str]
|
|
79
94
|
The name of the data source. If not specified, the name of the published
|
|
80
95
|
data source file is used.
|
|
81
96
|
|
|
97
|
+
owner: Optional[UserItem]
|
|
98
|
+
The owner of the data source.
|
|
99
|
+
|
|
82
100
|
owner_id : Optional[str]
|
|
83
101
|
The identifier of the owner of the data source.
|
|
84
102
|
|
|
103
|
+
project : Optional[ProjectItem]
|
|
104
|
+
The project that the data source belongs to.
|
|
105
|
+
|
|
85
106
|
project_id : Optional[str]
|
|
86
107
|
The identifier of the project associated with the data source. You must
|
|
87
108
|
provide this identifier when you create an instance of a DatasourceItem.
|
|
@@ -89,6 +110,9 @@ class DatasourceItem:
|
|
|
89
110
|
project_name : Optional[str]
|
|
90
111
|
The name of the project associated with the data source.
|
|
91
112
|
|
|
113
|
+
server_name : Optional[str]
|
|
114
|
+
The name of the server where the data source is published.
|
|
115
|
+
|
|
92
116
|
tags : Optional[set[str]]
|
|
93
117
|
The tags (list of strings) that have been added to the data source.
|
|
94
118
|
|
|
@@ -143,6 +167,13 @@ class DatasourceItem:
|
|
|
143
167
|
self.owner_id: Optional[str] = None
|
|
144
168
|
self.project_id: Optional[str] = project_id
|
|
145
169
|
self.tags: set[str] = set()
|
|
170
|
+
self._connected_workbooks_count: Optional[int] = None
|
|
171
|
+
self._favorites_total: Optional[int] = None
|
|
172
|
+
self._has_alert: Optional[bool] = None
|
|
173
|
+
self._is_published: Optional[bool] = None
|
|
174
|
+
self._server_name: Optional[str] = None
|
|
175
|
+
self._project: Optional[ProjectItem] = None
|
|
176
|
+
self._owner: Optional[UserItem] = None
|
|
146
177
|
|
|
147
178
|
self._permissions = None
|
|
148
179
|
self._data_quality_warnings = None
|
|
@@ -274,14 +305,42 @@ class DatasourceItem:
|
|
|
274
305
|
def size(self) -> Optional[int]:
|
|
275
306
|
return self._size
|
|
276
307
|
|
|
308
|
+
@property
|
|
309
|
+
def connected_workbooks_count(self) -> Optional[int]:
|
|
310
|
+
return self._connected_workbooks_count
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def favorites_total(self) -> Optional[int]:
|
|
314
|
+
return self._favorites_total
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def has_alert(self) -> Optional[bool]:
|
|
318
|
+
return self._has_alert
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def is_published(self) -> Optional[bool]:
|
|
322
|
+
return self._is_published
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def server_name(self) -> Optional[str]:
|
|
326
|
+
return self._server_name
|
|
327
|
+
|
|
328
|
+
@property
|
|
329
|
+
def project(self) -> Optional[ProjectItem]:
|
|
330
|
+
return self._project
|
|
331
|
+
|
|
332
|
+
@property
|
|
333
|
+
def owner(self) -> Optional[UserItem]:
|
|
334
|
+
return self._owner
|
|
335
|
+
|
|
277
336
|
def _set_connections(self, connections) -> None:
|
|
278
337
|
self._connections = connections
|
|
279
338
|
|
|
280
339
|
def _set_permissions(self, permissions):
|
|
281
340
|
self._permissions = permissions
|
|
282
341
|
|
|
283
|
-
def _set_data_quality_warnings(self,
|
|
284
|
-
self._data_quality_warnings =
|
|
342
|
+
def _set_data_quality_warnings(self, dqw):
|
|
343
|
+
self._data_quality_warnings = dqw
|
|
285
344
|
|
|
286
345
|
def _set_revisions(self, revisions):
|
|
287
346
|
self._revisions = revisions
|
|
@@ -310,6 +369,13 @@ class DatasourceItem:
|
|
|
310
369
|
use_remote_query_agent,
|
|
311
370
|
webpage_url,
|
|
312
371
|
size,
|
|
372
|
+
connected_workbooks_count,
|
|
373
|
+
favorites_total,
|
|
374
|
+
has_alert,
|
|
375
|
+
is_published,
|
|
376
|
+
server_name,
|
|
377
|
+
project,
|
|
378
|
+
owner,
|
|
313
379
|
) = self._parse_element(datasource_xml, ns)
|
|
314
380
|
self._set_values(
|
|
315
381
|
ask_data_enablement,
|
|
@@ -331,6 +397,13 @@ class DatasourceItem:
|
|
|
331
397
|
use_remote_query_agent,
|
|
332
398
|
webpage_url,
|
|
333
399
|
size,
|
|
400
|
+
connected_workbooks_count,
|
|
401
|
+
favorites_total,
|
|
402
|
+
has_alert,
|
|
403
|
+
is_published,
|
|
404
|
+
server_name,
|
|
405
|
+
project,
|
|
406
|
+
owner,
|
|
334
407
|
)
|
|
335
408
|
return self
|
|
336
409
|
|
|
@@ -355,6 +428,13 @@ class DatasourceItem:
|
|
|
355
428
|
use_remote_query_agent,
|
|
356
429
|
webpage_url,
|
|
357
430
|
size,
|
|
431
|
+
connected_workbooks_count,
|
|
432
|
+
favorites_total,
|
|
433
|
+
has_alert,
|
|
434
|
+
is_published,
|
|
435
|
+
server_name,
|
|
436
|
+
project,
|
|
437
|
+
owner,
|
|
358
438
|
):
|
|
359
439
|
if ask_data_enablement is not None:
|
|
360
440
|
self._ask_data_enablement = ask_data_enablement
|
|
@@ -394,6 +474,20 @@ class DatasourceItem:
|
|
|
394
474
|
self._webpage_url = webpage_url
|
|
395
475
|
if size is not None:
|
|
396
476
|
self._size = int(size)
|
|
477
|
+
if connected_workbooks_count is not None:
|
|
478
|
+
self._connected_workbooks_count = connected_workbooks_count
|
|
479
|
+
if favorites_total is not None:
|
|
480
|
+
self._favorites_total = favorites_total
|
|
481
|
+
if has_alert is not None:
|
|
482
|
+
self._has_alert = has_alert
|
|
483
|
+
if is_published is not None:
|
|
484
|
+
self._is_published = is_published
|
|
485
|
+
if server_name is not None:
|
|
486
|
+
self._server_name = server_name
|
|
487
|
+
if project is not None:
|
|
488
|
+
self._project = project
|
|
489
|
+
if owner is not None:
|
|
490
|
+
self._owner = owner
|
|
397
491
|
|
|
398
492
|
@classmethod
|
|
399
493
|
def from_response(cls, resp: str, ns: dict) -> list["DatasourceItem"]:
|
|
@@ -428,6 +522,11 @@ class DatasourceItem:
|
|
|
428
522
|
use_remote_query_agent = datasource_xml.get("useRemoteQueryAgent", None)
|
|
429
523
|
webpage_url = datasource_xml.get("webpageUrl", None)
|
|
430
524
|
size = datasource_xml.get("size", None)
|
|
525
|
+
connected_workbooks_count = nullable_str_to_int(datasource_xml.get("connectedWorkbooksCount", None))
|
|
526
|
+
favorites_total = nullable_str_to_int(datasource_xml.get("favoritesTotal", None))
|
|
527
|
+
has_alert = nullable_str_to_bool(datasource_xml.get("hasAlert", None))
|
|
528
|
+
is_published = nullable_str_to_bool(datasource_xml.get("isPublished", None))
|
|
529
|
+
server_name = datasource_xml.get("serverName", None)
|
|
431
530
|
|
|
432
531
|
tags = None
|
|
433
532
|
tags_elem = datasource_xml.find(".//t:tags", namespaces=ns)
|
|
@@ -438,12 +537,14 @@ class DatasourceItem:
|
|
|
438
537
|
project_name = None
|
|
439
538
|
project_elem = datasource_xml.find(".//t:project", namespaces=ns)
|
|
440
539
|
if project_elem is not None:
|
|
540
|
+
project = ProjectItem.from_xml(project_elem, ns)
|
|
441
541
|
project_id = project_elem.get("id", None)
|
|
442
542
|
project_name = project_elem.get("name", None)
|
|
443
543
|
|
|
444
544
|
owner_id = None
|
|
445
545
|
owner_elem = datasource_xml.find(".//t:owner", namespaces=ns)
|
|
446
546
|
if owner_elem is not None:
|
|
547
|
+
owner = UserItem.from_xml(owner_elem, ns)
|
|
447
548
|
owner_id = owner_elem.get("id", None)
|
|
448
549
|
|
|
449
550
|
ask_data_enablement = None
|
|
@@ -471,4 +572,11 @@ class DatasourceItem:
|
|
|
471
572
|
use_remote_query_agent,
|
|
472
573
|
webpage_url,
|
|
473
574
|
size,
|
|
575
|
+
connected_workbooks_count,
|
|
576
|
+
favorites_total,
|
|
577
|
+
has_alert,
|
|
578
|
+
is_published,
|
|
579
|
+
server_name,
|
|
580
|
+
project,
|
|
581
|
+
owner,
|
|
474
582
|
)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from typing import Optional, List
|
|
2
|
+
from defusedxml.ElementTree import fromstring
|
|
3
|
+
import xml.etree.ElementTree as ET
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ExtractItem:
|
|
7
|
+
"""
|
|
8
|
+
An extract refresh task item.
|
|
9
|
+
|
|
10
|
+
Attributes
|
|
11
|
+
----------
|
|
12
|
+
id : str
|
|
13
|
+
The ID of the extract refresh task
|
|
14
|
+
priority : int
|
|
15
|
+
The priority of the task
|
|
16
|
+
type : str
|
|
17
|
+
The type of extract refresh (incremental or full)
|
|
18
|
+
workbook_id : str, optional
|
|
19
|
+
The ID of the workbook if this is a workbook extract
|
|
20
|
+
datasource_id : str, optional
|
|
21
|
+
The ID of the datasource if this is a datasource extract
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self, priority: int, refresh_type: str, workbook_id: Optional[str] = None, datasource_id: Optional[str] = None
|
|
26
|
+
):
|
|
27
|
+
self._id: Optional[str] = None
|
|
28
|
+
self._priority = priority
|
|
29
|
+
self._type = refresh_type
|
|
30
|
+
self._workbook_id = workbook_id
|
|
31
|
+
self._datasource_id = datasource_id
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def id(self) -> Optional[str]:
|
|
35
|
+
return self._id
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def priority(self) -> int:
|
|
39
|
+
return self._priority
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def type(self) -> str:
|
|
43
|
+
return self._type
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def workbook_id(self) -> Optional[str]:
|
|
47
|
+
return self._workbook_id
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def datasource_id(self) -> Optional[str]:
|
|
51
|
+
return self._datasource_id
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def from_response(cls, resp: str, ns: dict) -> List["ExtractItem"]:
|
|
55
|
+
"""Create ExtractItem objects from XML response."""
|
|
56
|
+
parsed_response = fromstring(resp)
|
|
57
|
+
return cls.from_xml_element(parsed_response, ns)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_xml_element(cls, parsed_response: ET.Element, ns: dict) -> List["ExtractItem"]:
|
|
61
|
+
"""Create ExtractItem objects from XML element."""
|
|
62
|
+
all_extract_items = []
|
|
63
|
+
all_extract_xml = parsed_response.findall(".//t:extract", namespaces=ns)
|
|
64
|
+
|
|
65
|
+
for extract_xml in all_extract_xml:
|
|
66
|
+
extract_id = extract_xml.get("id", None)
|
|
67
|
+
priority = int(extract_xml.get("priority", 0))
|
|
68
|
+
refresh_type = extract_xml.get("type", "")
|
|
69
|
+
|
|
70
|
+
# Check for workbook or datasource
|
|
71
|
+
workbook_elem = extract_xml.find(".//t:workbook", namespaces=ns)
|
|
72
|
+
datasource_elem = extract_xml.find(".//t:datasource", namespaces=ns)
|
|
73
|
+
|
|
74
|
+
workbook_id = workbook_elem.get("id", None) if workbook_elem is not None else None
|
|
75
|
+
datasource_id = datasource_elem.get("id", None) if datasource_elem is not None else None
|
|
76
|
+
|
|
77
|
+
extract_item = cls(priority, refresh_type, workbook_id, datasource_id)
|
|
78
|
+
extract_item._id = extract_id
|
|
79
|
+
|
|
80
|
+
all_extract_items.append(extract_item)
|
|
81
|
+
|
|
82
|
+
return all_extract_items
|
|
@@ -146,8 +146,8 @@ class FlowItem:
|
|
|
146
146
|
def _set_permissions(self, permissions):
|
|
147
147
|
self._permissions = permissions
|
|
148
148
|
|
|
149
|
-
def _set_data_quality_warnings(self,
|
|
150
|
-
self._data_quality_warnings =
|
|
149
|
+
def _set_data_quality_warnings(self, dqw):
|
|
150
|
+
self._data_quality_warnings = dqw
|
|
151
151
|
|
|
152
152
|
def _parse_common_elements(self, flow_xml, ns):
|
|
153
153
|
if not isinstance(flow_xml, ET.Element):
|
|
@@ -44,6 +44,11 @@ class GroupItem:
|
|
|
44
44
|
login to a site. When the mode is onSync, a license is granted for group
|
|
45
45
|
members each time the domain is synced.
|
|
46
46
|
|
|
47
|
+
Attributes
|
|
48
|
+
----------
|
|
49
|
+
user_count: Optional[int]
|
|
50
|
+
The number of users in the group.
|
|
51
|
+
|
|
47
52
|
Examples
|
|
48
53
|
--------
|
|
49
54
|
>>> # Create a new group item
|
|
@@ -65,6 +70,7 @@ class GroupItem:
|
|
|
65
70
|
self._users: Optional[Callable[..., "Pager"]] = None
|
|
66
71
|
self.name: Optional[str] = name
|
|
67
72
|
self.domain_name: Optional[str] = domain_name
|
|
73
|
+
self._user_count: Optional[int] = None
|
|
68
74
|
|
|
69
75
|
def __repr__(self):
|
|
70
76
|
return f"{self.__class__.__name__}({self.__dict__!r})"
|
|
@@ -118,6 +124,10 @@ class GroupItem:
|
|
|
118
124
|
def _set_users(self, users: Callable[..., "Pager"]) -> None:
|
|
119
125
|
self._users = users
|
|
120
126
|
|
|
127
|
+
@property
|
|
128
|
+
def user_count(self) -> Optional[int]:
|
|
129
|
+
return self._user_count
|
|
130
|
+
|
|
121
131
|
@classmethod
|
|
122
132
|
def from_response(cls, resp, ns) -> list["GroupItem"]:
|
|
123
133
|
all_group_items = list()
|
|
@@ -127,6 +137,7 @@ class GroupItem:
|
|
|
127
137
|
name = group_xml.get("name", None)
|
|
128
138
|
group_item = cls(name)
|
|
129
139
|
group_item._id = group_xml.get("id", None)
|
|
140
|
+
group_item._user_count = int(count) if (count := group_xml.get("userCount", None)) else None
|
|
130
141
|
|
|
131
142
|
# Domain name is returned in a domain element for some calls
|
|
132
143
|
domain_elem = group_xml.find(".//t:domain", namespaces=ns)
|
|
@@ -2,6 +2,13 @@ from .property_decorators import property_is_valid_time, property_not_nullable
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class IntervalItem:
|
|
5
|
+
"""
|
|
6
|
+
This class sets the frequency and start time of the scheduled item. This
|
|
7
|
+
class contains the classes for the hourly, daily, weekly, and monthly
|
|
8
|
+
intervals. This class mirrors the options you can set using the REST API and
|
|
9
|
+
the Tableau Server interface.
|
|
10
|
+
"""
|
|
11
|
+
|
|
5
12
|
class Frequency:
|
|
6
13
|
Hourly = "Hourly"
|
|
7
14
|
Daily = "Daily"
|
|
@@ -26,6 +33,19 @@ class IntervalItem:
|
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
class HourlyInterval:
|
|
36
|
+
"""
|
|
37
|
+
Runs scheduled item hourly. To set the hourly interval, you create an
|
|
38
|
+
instance of the HourlyInterval class and assign the following values:
|
|
39
|
+
start_time, end_time, and interval_value. To set the start_time and
|
|
40
|
+
end_time, assign the time value using this syntax: start_time=time(hour, minute)
|
|
41
|
+
and end_time=time(hour, minute). The hour is specified in 24 hour time.
|
|
42
|
+
The interval_value specifies how often the to run the task within the
|
|
43
|
+
start and end time. The options are expressed in hours. For example,
|
|
44
|
+
interval_value=.25 is every 15 minutes. The values are .25, .5, 1, 2, 4, 6,
|
|
45
|
+
8, 12. Hourly schedules that run more frequently than every 60 minutes must
|
|
46
|
+
have start and end times that are on the hour.
|
|
47
|
+
"""
|
|
48
|
+
|
|
29
49
|
def __init__(self, start_time, end_time, interval_value):
|
|
30
50
|
self.start_time = start_time
|
|
31
51
|
self.end_time = end_time
|
|
@@ -109,6 +129,12 @@ class HourlyInterval:
|
|
|
109
129
|
|
|
110
130
|
|
|
111
131
|
class DailyInterval:
|
|
132
|
+
"""
|
|
133
|
+
Runs the scheduled item daily. To set the daily interval, you create an
|
|
134
|
+
instance of the DailyInterval and assign the start_time. The start time uses
|
|
135
|
+
the syntax start_time=time(hour, minute).
|
|
136
|
+
"""
|
|
137
|
+
|
|
112
138
|
def __init__(self, start_time, *interval_values):
|
|
113
139
|
self.start_time = start_time
|
|
114
140
|
self.interval = interval_values
|
|
@@ -177,6 +203,15 @@ class DailyInterval:
|
|
|
177
203
|
|
|
178
204
|
|
|
179
205
|
class WeeklyInterval:
|
|
206
|
+
"""
|
|
207
|
+
Runs the scheduled item once a week. To set the weekly interval, you create
|
|
208
|
+
an instance of the WeeklyInterval and assign the start time and multiple
|
|
209
|
+
instances for the interval_value (days of week and start time). The start
|
|
210
|
+
time uses the syntax time(hour, minute). The interval_value is the day of
|
|
211
|
+
the week, expressed as a IntervalItem. For example
|
|
212
|
+
TSC.IntervalItem.Day.Monday for Monday.
|
|
213
|
+
"""
|
|
214
|
+
|
|
180
215
|
def __init__(self, start_time, *interval_values):
|
|
181
216
|
self.start_time = start_time
|
|
182
217
|
self.interval = interval_values
|
|
@@ -214,6 +249,11 @@ class WeeklyInterval:
|
|
|
214
249
|
|
|
215
250
|
|
|
216
251
|
class MonthlyInterval:
|
|
252
|
+
"""
|
|
253
|
+
Runs the scheduled item once a month. To set the monthly interval, you
|
|
254
|
+
create an instance of the MonthlyInterval and assign the start time and day.
|
|
255
|
+
"""
|
|
256
|
+
|
|
217
257
|
def __init__(self, start_time, interval_value):
|
|
218
258
|
self.start_time = start_time
|
|
219
259
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import xml.etree.ElementTree as ET
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LocationItem:
|
|
6
|
+
"""
|
|
7
|
+
Details of where an item is located, such as a personal space or project.
|
|
8
|
+
|
|
9
|
+
Attributes
|
|
10
|
+
----------
|
|
11
|
+
id : str | None
|
|
12
|
+
The ID of the location.
|
|
13
|
+
|
|
14
|
+
type : str | None
|
|
15
|
+
The type of location, such as PersonalSpace or Project.
|
|
16
|
+
|
|
17
|
+
name : str | None
|
|
18
|
+
The name of the location.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
class Type:
|
|
22
|
+
PersonalSpace = "PersonalSpace"
|
|
23
|
+
Project = "Project"
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self._id: Optional[str] = None
|
|
27
|
+
self._type: Optional[str] = None
|
|
28
|
+
self._name: Optional[str] = None
|
|
29
|
+
|
|
30
|
+
def __repr__(self):
|
|
31
|
+
return f"{self.__class__.__name__}({self.__dict__!r})"
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def id(self) -> Optional[str]:
|
|
35
|
+
return self._id
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def type(self) -> Optional[str]:
|
|
39
|
+
return self._type
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def name(self) -> Optional[str]:
|
|
43
|
+
return self._name
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def from_xml(cls, xml: ET.Element, ns: Optional[dict] = None) -> "LocationItem":
|
|
47
|
+
if ns is None:
|
|
48
|
+
ns = {}
|
|
49
|
+
location = cls()
|
|
50
|
+
location._id = xml.get("id", None)
|
|
51
|
+
location._type = xml.get("type", None)
|
|
52
|
+
location._name = xml.get("name", None)
|
|
53
|
+
return location
|