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,7 +1,7 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import datetime
|
|
3
3
|
import xml.etree.ElementTree as ET
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Iterable, Optional
|
|
5
5
|
|
|
6
6
|
from defusedxml.ElementTree import fromstring
|
|
7
7
|
|
|
@@ -14,9 +14,54 @@ from tableauserverclient.models.property_decorators import property_not_nullable
|
|
|
14
14
|
from tableauserverclient.models.tag_item import TagItem
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class FlowItem
|
|
17
|
+
class FlowItem:
|
|
18
|
+
"""
|
|
19
|
+
Represents a Tableau Flow item.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
project_id: str
|
|
24
|
+
The ID of the project that the flow belongs to.
|
|
25
|
+
|
|
26
|
+
name: Optional[str]
|
|
27
|
+
The name of the flow.
|
|
28
|
+
|
|
29
|
+
Attributes
|
|
30
|
+
----------
|
|
31
|
+
connections: Iterable[ConnectionItem]
|
|
32
|
+
The connections associated with the flow. This property is not populated
|
|
33
|
+
by default and must be populated by calling the `populate_connections`
|
|
34
|
+
method.
|
|
35
|
+
|
|
36
|
+
created_at: Optional[datetime.datetime]
|
|
37
|
+
The date and time when the flow was created.
|
|
38
|
+
|
|
39
|
+
description: Optional[str]
|
|
40
|
+
The description of the flow.
|
|
41
|
+
|
|
42
|
+
dqws: Iterable[DQWItem]
|
|
43
|
+
The data quality warnings associated with the flow. This property is not
|
|
44
|
+
populated by default and must be populated by calling the `populate_dqws`
|
|
45
|
+
method.
|
|
46
|
+
|
|
47
|
+
id: Optional[str]
|
|
48
|
+
The ID of the flow.
|
|
49
|
+
|
|
50
|
+
name: Optional[str]
|
|
51
|
+
The name of the flow.
|
|
52
|
+
|
|
53
|
+
owner_id: Optional[str]
|
|
54
|
+
The ID of the user who owns the flow.
|
|
55
|
+
|
|
56
|
+
project_name: Optional[str]
|
|
57
|
+
The name of the project that the flow belongs to.
|
|
58
|
+
|
|
59
|
+
tags: set[str]
|
|
60
|
+
The tags associated with the flow.
|
|
61
|
+
"""
|
|
62
|
+
|
|
18
63
|
def __repr__(self):
|
|
19
|
-
return "<Flow {
|
|
64
|
+
return "<Flow {} '{}' ({}) Project={} createdAt={}".format(
|
|
20
65
|
self._id, self.name, self.description, self.project_id, self.created_at
|
|
21
66
|
)
|
|
22
67
|
|
|
@@ -24,18 +69,18 @@ class FlowItem(object):
|
|
|
24
69
|
self._webpage_url: Optional[str] = None
|
|
25
70
|
self._created_at: Optional[datetime.datetime] = None
|
|
26
71
|
self._id: Optional[str] = None
|
|
27
|
-
self._initial_tags:
|
|
72
|
+
self._initial_tags: set[str] = set()
|
|
28
73
|
self._project_name: Optional[str] = None
|
|
29
74
|
self._updated_at: Optional[datetime.datetime] = None
|
|
30
75
|
self.name: Optional[str] = name
|
|
31
76
|
self.owner_id: Optional[str] = None
|
|
32
77
|
self.project_id: str = project_id
|
|
33
|
-
self.tags:
|
|
78
|
+
self.tags: set[str] = set()
|
|
34
79
|
self.description: Optional[str] = None
|
|
35
80
|
|
|
36
|
-
self._connections: Optional[ConnectionItem] = None
|
|
37
|
-
self._permissions: Optional[Permission] = None
|
|
38
|
-
self._data_quality_warnings: Optional[DQWItem] = None
|
|
81
|
+
self._connections: Optional[Iterable[ConnectionItem]] = None
|
|
82
|
+
self._permissions: Optional[Iterable[Permission]] = None
|
|
83
|
+
self._data_quality_warnings: Optional[Iterable[DQWItem]] = None
|
|
39
84
|
|
|
40
85
|
@property
|
|
41
86
|
def connections(self):
|
|
@@ -170,7 +215,7 @@ class FlowItem(object):
|
|
|
170
215
|
self.owner_id = owner_id
|
|
171
216
|
|
|
172
217
|
@classmethod
|
|
173
|
-
def from_response(cls, resp, ns) ->
|
|
218
|
+
def from_response(cls, resp, ns) -> list["FlowItem"]:
|
|
174
219
|
all_flow_items = list()
|
|
175
220
|
parsed_response = fromstring(resp)
|
|
176
221
|
all_flow_xml = parsed_response.findall(".//t:flow", namespaces=ns)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from defusedxml.ElementTree import fromstring
|
|
6
6
|
|
|
7
7
|
from tableauserverclient.datetime_helpers import parse_datetime
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class FlowRunItem
|
|
10
|
+
class FlowRunItem:
|
|
11
11
|
def __init__(self) -> None:
|
|
12
12
|
self._id: str = ""
|
|
13
13
|
self._flow_id: Optional[str] = None
|
|
@@ -71,7 +71,7 @@ class FlowRunItem(object):
|
|
|
71
71
|
self._background_job_id = background_job_id
|
|
72
72
|
|
|
73
73
|
@classmethod
|
|
74
|
-
def from_response(cls:
|
|
74
|
+
def from_response(cls: type["FlowRunItem"], resp: bytes, ns: Optional[dict]) -> list["FlowRunItem"]:
|
|
75
75
|
all_flowrun_items = list()
|
|
76
76
|
parsed_response = fromstring(resp)
|
|
77
77
|
all_flowrun_xml = itertools.chain(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Callable,
|
|
1
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
|
2
2
|
|
|
3
3
|
from defusedxml.ElementTree import fromstring
|
|
4
4
|
|
|
@@ -11,7 +11,47 @@ if TYPE_CHECKING:
|
|
|
11
11
|
from tableauserverclient.server import Pager
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class GroupItem
|
|
14
|
+
class GroupItem:
|
|
15
|
+
"""
|
|
16
|
+
The GroupItem class contains the attributes for the group resources on
|
|
17
|
+
Tableau Server. The GroupItem class defines the information you can request
|
|
18
|
+
or query from Tableau Server. The class members correspond to the attributes
|
|
19
|
+
of a server request or response payload.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
name: str
|
|
24
|
+
The name of the group.
|
|
25
|
+
|
|
26
|
+
domain_name: str
|
|
27
|
+
The name of the Active Directory domain ("local" if local authentication is used).
|
|
28
|
+
|
|
29
|
+
Properties
|
|
30
|
+
----------
|
|
31
|
+
users: Pager[UserItem]
|
|
32
|
+
The users in the group. Must be populated with a call to `populate_users()`.
|
|
33
|
+
|
|
34
|
+
id: str
|
|
35
|
+
The unique identifier for the group.
|
|
36
|
+
|
|
37
|
+
minimum_site_role: str
|
|
38
|
+
The minimum site role for users in the group. Use the `UserItem.Roles` enum.
|
|
39
|
+
Users in the group cannot have their site role set lower than this value.
|
|
40
|
+
|
|
41
|
+
license_mode: str
|
|
42
|
+
The mode defining when to apply licenses for group members. When the
|
|
43
|
+
mode is onLogin, a license is granted for each group member when they
|
|
44
|
+
login to a site. When the mode is onSync, a license is granted for group
|
|
45
|
+
members each time the domain is synced.
|
|
46
|
+
|
|
47
|
+
Examples
|
|
48
|
+
--------
|
|
49
|
+
>>> # Create a new group item
|
|
50
|
+
>>> newgroup = TSC.GroupItem('My Group')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
|
|
15
55
|
tag_name: str = "group"
|
|
16
56
|
|
|
17
57
|
class LicenseMode:
|
|
@@ -27,7 +67,7 @@ class GroupItem(object):
|
|
|
27
67
|
self.domain_name: Optional[str] = domain_name
|
|
28
68
|
|
|
29
69
|
def __repr__(self):
|
|
30
|
-
return "{
|
|
70
|
+
return f"{self.__class__.__name__}({self.__dict__!r})"
|
|
31
71
|
|
|
32
72
|
@property
|
|
33
73
|
def domain_name(self) -> Optional[str]:
|
|
@@ -79,7 +119,7 @@ class GroupItem(object):
|
|
|
79
119
|
self._users = users
|
|
80
120
|
|
|
81
121
|
@classmethod
|
|
82
|
-
def from_response(cls, resp, ns) ->
|
|
122
|
+
def from_response(cls, resp, ns) -> list["GroupItem"]:
|
|
83
123
|
all_group_items = list()
|
|
84
124
|
parsed_response = fromstring(resp)
|
|
85
125
|
all_group_xml = parsed_response.findall(".//t:group", namespaces=ns)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
import xml.etree.ElementTree as ET
|
|
3
3
|
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
@@ -13,7 +13,7 @@ class GroupSetItem:
|
|
|
13
13
|
def __init__(self, name: Optional[str] = None) -> None:
|
|
14
14
|
self.name = name
|
|
15
15
|
self.id: Optional[str] = None
|
|
16
|
-
self.groups:
|
|
16
|
+
self.groups: list["GroupItem"] = []
|
|
17
17
|
self.group_count: int = 0
|
|
18
18
|
|
|
19
19
|
def __str__(self) -> str:
|
|
@@ -25,13 +25,13 @@ class GroupSetItem:
|
|
|
25
25
|
return self.__str__()
|
|
26
26
|
|
|
27
27
|
@classmethod
|
|
28
|
-
def from_response(cls, response: bytes, ns:
|
|
28
|
+
def from_response(cls, response: bytes, ns: dict[str, str]) -> list["GroupSetItem"]:
|
|
29
29
|
parsed_response = fromstring(response)
|
|
30
30
|
all_groupset_xml = parsed_response.findall(".//t:groupSet", namespaces=ns)
|
|
31
31
|
return [cls.from_xml(xml, ns) for xml in all_groupset_xml]
|
|
32
32
|
|
|
33
33
|
@classmethod
|
|
34
|
-
def from_xml(cls, groupset_xml: ET.Element, ns:
|
|
34
|
+
def from_xml(cls, groupset_xml: ET.Element, ns: dict[str, str]) -> "GroupSetItem":
|
|
35
35
|
def get_group(group_xml: ET.Element) -> GroupItem:
|
|
36
36
|
group_item = GroupItem()
|
|
37
37
|
group_item._id = group_xml.get("id")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from .property_decorators import property_is_valid_time, property_not_nullable
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
class IntervalItem
|
|
4
|
+
class IntervalItem:
|
|
5
5
|
class Frequency:
|
|
6
6
|
Hourly = "Hourly"
|
|
7
7
|
Daily = "Daily"
|
|
@@ -25,7 +25,7 @@ class IntervalItem(object):
|
|
|
25
25
|
LastDay = "LastDay"
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class HourlyInterval
|
|
28
|
+
class HourlyInterval:
|
|
29
29
|
def __init__(self, start_time, end_time, interval_value):
|
|
30
30
|
self.start_time = start_time
|
|
31
31
|
self.end_time = end_time
|
|
@@ -73,12 +73,12 @@ class HourlyInterval(object):
|
|
|
73
73
|
for interval in intervals:
|
|
74
74
|
# if an hourly interval is a string, then it is a weekDay interval
|
|
75
75
|
if isinstance(interval, str) and not interval.isnumeric() and not hasattr(IntervalItem.Day, interval):
|
|
76
|
-
error = "Invalid weekDay interval {}"
|
|
76
|
+
error = f"Invalid weekDay interval {interval}"
|
|
77
77
|
raise ValueError(error)
|
|
78
78
|
|
|
79
79
|
# if an hourly interval is a number, it is an hours or minutes interval
|
|
80
80
|
if isinstance(interval, (int, float)) and float(interval) not in VALID_INTERVALS:
|
|
81
|
-
error = "Invalid interval {} not in {
|
|
81
|
+
error = f"Invalid interval {interval} not in {str(VALID_INTERVALS)}"
|
|
82
82
|
raise ValueError(error)
|
|
83
83
|
|
|
84
84
|
self._interval = intervals
|
|
@@ -108,7 +108,7 @@ class HourlyInterval(object):
|
|
|
108
108
|
return interval_type_pairs
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
class DailyInterval
|
|
111
|
+
class DailyInterval:
|
|
112
112
|
def __init__(self, start_time, *interval_values):
|
|
113
113
|
self.start_time = start_time
|
|
114
114
|
self.interval = interval_values
|
|
@@ -141,12 +141,12 @@ class DailyInterval(object):
|
|
|
141
141
|
for interval in intervals:
|
|
142
142
|
# if an hourly interval is a string, then it is a weekDay interval
|
|
143
143
|
if isinstance(interval, str) and not interval.isnumeric() and not hasattr(IntervalItem.Day, interval):
|
|
144
|
-
error = "Invalid weekDay interval {}"
|
|
144
|
+
error = f"Invalid weekDay interval {interval}"
|
|
145
145
|
raise ValueError(error)
|
|
146
146
|
|
|
147
147
|
# if an hourly interval is a number, it is an hours or minutes interval
|
|
148
148
|
if isinstance(interval, (int, float)) and float(interval) not in VALID_INTERVALS:
|
|
149
|
-
error = "Invalid interval {} not in {
|
|
149
|
+
error = f"Invalid interval {interval} not in {str(VALID_INTERVALS)}"
|
|
150
150
|
raise ValueError(error)
|
|
151
151
|
|
|
152
152
|
self._interval = intervals
|
|
@@ -176,7 +176,7 @@ class DailyInterval(object):
|
|
|
176
176
|
return interval_type_pairs
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
class WeeklyInterval
|
|
179
|
+
class WeeklyInterval:
|
|
180
180
|
def __init__(self, start_time, *interval_values):
|
|
181
181
|
self.start_time = start_time
|
|
182
182
|
self.interval = interval_values
|
|
@@ -213,7 +213,7 @@ class WeeklyInterval(object):
|
|
|
213
213
|
return [(IntervalItem.Occurrence.WeekDay, day) for day in self.interval]
|
|
214
214
|
|
|
215
215
|
|
|
216
|
-
class MonthlyInterval
|
|
216
|
+
class MonthlyInterval:
|
|
217
217
|
def __init__(self, start_time, interval_value):
|
|
218
218
|
self.start_time = start_time
|
|
219
219
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
5
5
|
|
|
@@ -7,7 +7,72 @@ from tableauserverclient.datetime_helpers import parse_datetime
|
|
|
7
7
|
from tableauserverclient.models.flow_run_item import FlowRunItem
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class JobItem
|
|
10
|
+
class JobItem:
|
|
11
|
+
"""
|
|
12
|
+
Using the TSC library, you can get information about an asynchronous process
|
|
13
|
+
(or job) on the server. These jobs can be created when Tableau runs certain
|
|
14
|
+
tasks that could be long running, such as importing or synchronizing users
|
|
15
|
+
from Active Directory, or running an extract refresh. For example, the REST
|
|
16
|
+
API methods to create or update groups, to run an extract refresh task, or
|
|
17
|
+
to publish workbooks can take an asJob parameter (asJob-true) that creates a
|
|
18
|
+
background process (the job) to complete the call. Information about the
|
|
19
|
+
asynchronous job is returned from the method.
|
|
20
|
+
|
|
21
|
+
If you have the identifier of the job, you can use the TSC library to find
|
|
22
|
+
out the status of the asynchronous job.
|
|
23
|
+
|
|
24
|
+
The job properties are defined in the JobItem class. The class corresponds
|
|
25
|
+
to the properties for jobs you can access using the Tableau Server REST API.
|
|
26
|
+
The job methods are based upon the endpoints for jobs in the REST API and
|
|
27
|
+
operate on the JobItem class.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
id_ : str
|
|
32
|
+
The identifier of the job.
|
|
33
|
+
|
|
34
|
+
job_type : str
|
|
35
|
+
The type of job.
|
|
36
|
+
|
|
37
|
+
progress : str
|
|
38
|
+
The progress of the job.
|
|
39
|
+
|
|
40
|
+
created_at : datetime.datetime
|
|
41
|
+
The date and time the job was created.
|
|
42
|
+
|
|
43
|
+
started_at : Optional[datetime.datetime]
|
|
44
|
+
The date and time the job was started.
|
|
45
|
+
|
|
46
|
+
completed_at : Optional[datetime.datetime]
|
|
47
|
+
The date and time the job was completed.
|
|
48
|
+
|
|
49
|
+
finish_code : int
|
|
50
|
+
The finish code of the job. 0 for success, 1 for failure, 2 for cancelled.
|
|
51
|
+
|
|
52
|
+
notes : Optional[list[str]]
|
|
53
|
+
Contains detailed notes about the job.
|
|
54
|
+
|
|
55
|
+
mode : Optional[str]
|
|
56
|
+
|
|
57
|
+
workbook_id : Optional[str]
|
|
58
|
+
The identifier of the workbook associated with the job.
|
|
59
|
+
|
|
60
|
+
datasource_id : Optional[str]
|
|
61
|
+
The identifier of the datasource associated with the job.
|
|
62
|
+
|
|
63
|
+
flow_run : Optional[FlowRunItem]
|
|
64
|
+
The flow run associated with the job.
|
|
65
|
+
|
|
66
|
+
updated_at : Optional[datetime.datetime]
|
|
67
|
+
The date and time the job was last updated.
|
|
68
|
+
|
|
69
|
+
workbook_name : Optional[str]
|
|
70
|
+
The name of the workbook associated with the job.
|
|
71
|
+
|
|
72
|
+
datasource_name : Optional[str]
|
|
73
|
+
The name of the datasource associated with the job.
|
|
74
|
+
"""
|
|
75
|
+
|
|
11
76
|
class FinishCode:
|
|
12
77
|
"""
|
|
13
78
|
Status codes as documented on
|
|
@@ -27,7 +92,7 @@ class JobItem(object):
|
|
|
27
92
|
started_at: Optional[datetime.datetime] = None,
|
|
28
93
|
completed_at: Optional[datetime.datetime] = None,
|
|
29
94
|
finish_code: int = 0,
|
|
30
|
-
notes: Optional[
|
|
95
|
+
notes: Optional[list[str]] = None,
|
|
31
96
|
mode: Optional[str] = None,
|
|
32
97
|
workbook_id: Optional[str] = None,
|
|
33
98
|
datasource_id: Optional[str] = None,
|
|
@@ -43,7 +108,7 @@ class JobItem(object):
|
|
|
43
108
|
self._started_at = started_at
|
|
44
109
|
self._completed_at = completed_at
|
|
45
110
|
self._finish_code = finish_code
|
|
46
|
-
self._notes:
|
|
111
|
+
self._notes: list[str] = notes or []
|
|
47
112
|
self._mode = mode
|
|
48
113
|
self._workbook_id = workbook_id
|
|
49
114
|
self._datasource_id = datasource_id
|
|
@@ -81,7 +146,7 @@ class JobItem(object):
|
|
|
81
146
|
return self._finish_code
|
|
82
147
|
|
|
83
148
|
@property
|
|
84
|
-
def notes(self) ->
|
|
149
|
+
def notes(self) -> list[str]:
|
|
85
150
|
return self._notes
|
|
86
151
|
|
|
87
152
|
@property
|
|
@@ -139,7 +204,7 @@ class JobItem(object):
|
|
|
139
204
|
return self.__str__() + " { " + ", ".join(" % s: % s" % item for item in vars(self).items()) + "}"
|
|
140
205
|
|
|
141
206
|
@classmethod
|
|
142
|
-
def from_response(cls, xml, ns) ->
|
|
207
|
+
def from_response(cls, xml, ns) -> list["JobItem"]:
|
|
143
208
|
parsed_response = fromstring(xml)
|
|
144
209
|
all_tasks_xml = parsed_response.findall(".//t:job", namespaces=ns)
|
|
145
210
|
|
|
@@ -191,7 +256,7 @@ class JobItem(object):
|
|
|
191
256
|
)
|
|
192
257
|
|
|
193
258
|
|
|
194
|
-
class BackgroundJobItem
|
|
259
|
+
class BackgroundJobItem:
|
|
195
260
|
class Status:
|
|
196
261
|
Pending: str = "Pending"
|
|
197
262
|
InProgress: str = "InProgress"
|
|
@@ -270,7 +335,7 @@ class BackgroundJobItem(object):
|
|
|
270
335
|
return self._priority
|
|
271
336
|
|
|
272
337
|
@classmethod
|
|
273
|
-
def from_response(cls, xml, ns) ->
|
|
338
|
+
def from_response(cls, xml, ns) -> list["BackgroundJobItem"]:
|
|
274
339
|
parsed_response = fromstring(xml)
|
|
275
340
|
all_tasks_xml = parsed_response.findall(".//t:backgroundJob", namespaces=ns)
|
|
276
341
|
return [cls._parse_element(x, ns) for x in all_tasks_xml]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ class LinkedTaskItem:
|
|
|
14
14
|
self.schedule: Optional[ScheduleItem] = None
|
|
15
15
|
|
|
16
16
|
@classmethod
|
|
17
|
-
def from_response(cls, resp: bytes, namespace) ->
|
|
17
|
+
def from_response(cls, resp: bytes, namespace) -> list["LinkedTaskItem"]:
|
|
18
18
|
parsed_response = fromstring(resp)
|
|
19
19
|
return [
|
|
20
20
|
cls._parse_element(x, namespace)
|
|
@@ -35,10 +35,10 @@ class LinkedTaskStepItem:
|
|
|
35
35
|
self.id: Optional[str] = None
|
|
36
36
|
self.step_number: Optional[int] = None
|
|
37
37
|
self.stop_downstream_on_failure: Optional[bool] = None
|
|
38
|
-
self.task_details:
|
|
38
|
+
self.task_details: list[LinkedTaskFlowRunItem] = []
|
|
39
39
|
|
|
40
40
|
@classmethod
|
|
41
|
-
def from_task_xml(cls, xml, namespace) ->
|
|
41
|
+
def from_task_xml(cls, xml, namespace) -> list["LinkedTaskStepItem"]:
|
|
42
42
|
return [cls._parse_element(x, namespace) for x in xml.findall(".//t:linkedTaskSteps[@id]", namespace)]
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
@@ -61,7 +61,7 @@ class LinkedTaskFlowRunItem:
|
|
|
61
61
|
self.flow_name: Optional[str] = None
|
|
62
62
|
|
|
63
63
|
@classmethod
|
|
64
|
-
def _parse_element(cls, xml, namespace) ->
|
|
64
|
+
def _parse_element(cls, xml, namespace) -> list["LinkedTaskFlowRunItem"]:
|
|
65
65
|
all_tasks = []
|
|
66
66
|
for flow_run in xml.findall(".//t:flowRun[@id]", namespace):
|
|
67
67
|
task = cls()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import xml.etree.ElementTree as ET
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from tableauserverclient.datetime_helpers import parse_datetime
|
|
6
6
|
from .property_decorators import property_is_boolean, property_is_datetime
|
|
@@ -8,7 +8,7 @@ from .tag_item import TagItem
|
|
|
8
8
|
from .permissions_item import Permission
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class MetricItem
|
|
11
|
+
class MetricItem:
|
|
12
12
|
def __init__(self, name: Optional[str] = None):
|
|
13
13
|
self._id: Optional[str] = None
|
|
14
14
|
self._name: Optional[str] = name
|
|
@@ -21,8 +21,8 @@ class MetricItem(object):
|
|
|
21
21
|
self._project_name: Optional[str] = None
|
|
22
22
|
self._owner_id: Optional[str] = None
|
|
23
23
|
self._view_id: Optional[str] = None
|
|
24
|
-
self._initial_tags:
|
|
25
|
-
self.tags:
|
|
24
|
+
self._initial_tags: set[str] = set()
|
|
25
|
+
self.tags: set[str] = set()
|
|
26
26
|
self._permissions: Optional[Permission] = None
|
|
27
27
|
|
|
28
28
|
@property
|
|
@@ -126,7 +126,7 @@ class MetricItem(object):
|
|
|
126
126
|
cls,
|
|
127
127
|
resp: bytes,
|
|
128
128
|
ns,
|
|
129
|
-
) ->
|
|
129
|
+
) -> list["MetricItem"]:
|
|
130
130
|
all_metric_items = list()
|
|
131
131
|
parsed_response = ET.fromstring(resp)
|
|
132
132
|
all_metric_xml = parsed_response.findall(".//t:metric", namespaces=ns)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import xml.etree.ElementTree as ET
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
4
|
from defusedxml.ElementTree import fromstring
|
|
5
5
|
|
|
@@ -36,23 +36,25 @@ class Permission:
|
|
|
36
36
|
ShareView = "ShareView"
|
|
37
37
|
ViewComments = "ViewComments"
|
|
38
38
|
ViewUnderlyingData = "ViewUnderlyingData"
|
|
39
|
+
VizqlDataApiAccess = "VizqlDataApiAccess"
|
|
39
40
|
WebAuthoring = "WebAuthoring"
|
|
40
41
|
Write = "Write"
|
|
41
42
|
RunExplainData = "RunExplainData"
|
|
42
43
|
CreateRefreshMetrics = "CreateRefreshMetrics"
|
|
43
44
|
SaveAs = "SaveAs"
|
|
45
|
+
PulseMetricDefine = "PulseMetricDefine"
|
|
44
46
|
|
|
45
47
|
def __repr__(self):
|
|
46
48
|
return "<Enum Capability: AddComment | ChangeHierarchy | ChangePermission ... (17 more) >"
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
class PermissionsRule:
|
|
50
|
-
def __init__(self, grantee: ResourceReference, capabilities:
|
|
52
|
+
def __init__(self, grantee: ResourceReference, capabilities: dict[str, str]) -> None:
|
|
51
53
|
self.grantee = grantee
|
|
52
54
|
self.capabilities = capabilities
|
|
53
55
|
|
|
54
56
|
def __repr__(self):
|
|
55
|
-
return "<PermissionsRule grantee={}, capabilities={}>"
|
|
57
|
+
return f"<PermissionsRule grantee={self.grantee}, capabilities={self.capabilities}>"
|
|
56
58
|
|
|
57
59
|
def __eq__(self, other: object) -> bool:
|
|
58
60
|
if not hasattr(other, "grantee") or not hasattr(other, "capabilities"):
|
|
@@ -66,7 +68,7 @@ class PermissionsRule:
|
|
|
66
68
|
if self.capabilities == other.capabilities:
|
|
67
69
|
return self
|
|
68
70
|
|
|
69
|
-
capabilities =
|
|
71
|
+
capabilities = {*self.capabilities.keys(), *other.capabilities.keys()}
|
|
70
72
|
new_capabilities = {}
|
|
71
73
|
for capability in capabilities:
|
|
72
74
|
if (self.capabilities.get(capability), other.capabilities.get(capability)) == (
|
|
@@ -86,7 +88,7 @@ class PermissionsRule:
|
|
|
86
88
|
if self.capabilities == other.capabilities:
|
|
87
89
|
return self
|
|
88
90
|
|
|
89
|
-
capabilities =
|
|
91
|
+
capabilities = {*self.capabilities.keys(), *other.capabilities.keys()}
|
|
90
92
|
new_capabilities = {}
|
|
91
93
|
for capability in capabilities:
|
|
92
94
|
if Permission.Mode.Allow in (self.capabilities.get(capability), other.capabilities.get(capability)):
|
|
@@ -100,14 +102,14 @@ class PermissionsRule:
|
|
|
100
102
|
return PermissionsRule(self.grantee, new_capabilities)
|
|
101
103
|
|
|
102
104
|
@classmethod
|
|
103
|
-
def from_response(cls, resp, ns=None) ->
|
|
105
|
+
def from_response(cls, resp, ns=None) -> list["PermissionsRule"]:
|
|
104
106
|
parsed_response = fromstring(resp)
|
|
105
107
|
|
|
106
108
|
rules = []
|
|
107
109
|
permissions_rules_list_xml = parsed_response.findall(".//t:granteeCapabilities", namespaces=ns)
|
|
108
110
|
|
|
109
111
|
for grantee_capability_xml in permissions_rules_list_xml:
|
|
110
|
-
capability_dict:
|
|
112
|
+
capability_dict: dict[str, str] = {}
|
|
111
113
|
|
|
112
114
|
grantee = PermissionsRule._parse_grantee_element(grantee_capability_xml, ns)
|
|
113
115
|
|
|
@@ -116,7 +118,7 @@ class PermissionsRule:
|
|
|
116
118
|
mode = capability_xml.get("mode")
|
|
117
119
|
|
|
118
120
|
if name is None or mode is None:
|
|
119
|
-
logger.error("Capability was not valid: {}"
|
|
121
|
+
logger.error(f"Capability was not valid: {capability_xml}")
|
|
120
122
|
raise UnpopulatedPropertyError()
|
|
121
123
|
else:
|
|
122
124
|
capability_dict[name] = mode
|
|
@@ -127,7 +129,7 @@ class PermissionsRule:
|
|
|
127
129
|
return rules
|
|
128
130
|
|
|
129
131
|
@staticmethod
|
|
130
|
-
def _parse_grantee_element(grantee_capability_xml: ET.Element, ns: Optional[
|
|
132
|
+
def _parse_grantee_element(grantee_capability_xml: ET.Element, ns: Optional[dict[str, str]]) -> ResourceReference:
|
|
131
133
|
"""Use Xpath magic and some string splitting to get the right object type from the xml"""
|
|
132
134
|
|
|
133
135
|
# Get the first element in the tree with an 'id' attribute
|
|
@@ -146,6 +148,6 @@ class PermissionsRule:
|
|
|
146
148
|
elif grantee_type == "groupSet":
|
|
147
149
|
grantee = GroupSetItem.as_reference(grantee_id)
|
|
148
150
|
else:
|
|
149
|
-
raise UnknownGranteeTypeError("No support for grantee type of {}"
|
|
151
|
+
raise UnknownGranteeTypeError(f"No support for grantee type of {grantee_type}")
|
|
150
152
|
|
|
151
153
|
return grantee
|