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
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
import xml.etree.ElementTree as ET
|
|
3
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional, overload
|
|
4
3
|
|
|
5
4
|
from defusedxml.ElementTree import fromstring
|
|
6
5
|
|
|
7
6
|
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
|
|
8
|
-
from tableauserverclient.models.property_decorators import property_is_enum
|
|
7
|
+
from tableauserverclient.models.property_decorators import property_is_enum
|
|
8
|
+
from tableauserverclient.models.user_item import UserItem
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class ProjectItem:
|
|
@@ -39,12 +39,32 @@ class ProjectItem:
|
|
|
39
39
|
|
|
40
40
|
Attributes
|
|
41
41
|
----------
|
|
42
|
+
datasource_count : int
|
|
43
|
+
The number of data sources in the project.
|
|
44
|
+
|
|
42
45
|
id : str
|
|
43
46
|
The unique identifier for the project.
|
|
44
47
|
|
|
48
|
+
owner: Optional[UserItem]
|
|
49
|
+
The UserItem owner of the project.
|
|
50
|
+
|
|
45
51
|
owner_id : str
|
|
46
52
|
The unique identifier for the UserItem owner of the project.
|
|
47
53
|
|
|
54
|
+
project_count : int
|
|
55
|
+
The number of projects in the project.
|
|
56
|
+
|
|
57
|
+
top_level_project : bool
|
|
58
|
+
True if the project is a top-level project.
|
|
59
|
+
|
|
60
|
+
view_count : int
|
|
61
|
+
The number of views in the project.
|
|
62
|
+
|
|
63
|
+
workbook_count : int
|
|
64
|
+
The number of workbooks in the project.
|
|
65
|
+
|
|
66
|
+
writeable : bool
|
|
67
|
+
True if the project is writeable.
|
|
48
68
|
"""
|
|
49
69
|
|
|
50
70
|
ERROR_MSG = "Project item must be populated with permissions first."
|
|
@@ -75,6 +95,8 @@ class ProjectItem:
|
|
|
75
95
|
self.parent_id: Optional[str] = parent_id
|
|
76
96
|
self._samples: Optional[bool] = samples
|
|
77
97
|
self._owner_id: Optional[str] = None
|
|
98
|
+
self._top_level_project: Optional[bool] = None
|
|
99
|
+
self._writeable: Optional[bool] = None
|
|
78
100
|
|
|
79
101
|
self._permissions = None
|
|
80
102
|
self._default_workbook_permissions = None
|
|
@@ -87,6 +109,13 @@ class ProjectItem:
|
|
|
87
109
|
self._default_database_permissions = None
|
|
88
110
|
self._default_table_permissions = None
|
|
89
111
|
|
|
112
|
+
self._project_count: Optional[int] = None
|
|
113
|
+
self._workbook_count: Optional[int] = None
|
|
114
|
+
self._view_count: Optional[int] = None
|
|
115
|
+
self._datasource_count: Optional[int] = None
|
|
116
|
+
|
|
117
|
+
self._owner: Optional[UserItem] = None
|
|
118
|
+
|
|
90
119
|
@property
|
|
91
120
|
def content_permissions(self):
|
|
92
121
|
return self._content_permissions
|
|
@@ -176,25 +205,53 @@ class ProjectItem:
|
|
|
176
205
|
def owner_id(self, value: str) -> None:
|
|
177
206
|
self._owner_id = value
|
|
178
207
|
|
|
208
|
+
@property
|
|
209
|
+
def top_level_project(self) -> Optional[bool]:
|
|
210
|
+
return self._top_level_project
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def writeable(self) -> Optional[bool]:
|
|
214
|
+
return self._writeable
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def project_count(self) -> Optional[int]:
|
|
218
|
+
return self._project_count
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def workbook_count(self) -> Optional[int]:
|
|
222
|
+
return self._workbook_count
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def view_count(self) -> Optional[int]:
|
|
226
|
+
return self._view_count
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def datasource_count(self) -> Optional[int]:
|
|
230
|
+
return self._datasource_count
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def owner(self) -> Optional[UserItem]:
|
|
234
|
+
return self._owner
|
|
235
|
+
|
|
179
236
|
def is_default(self):
|
|
180
237
|
return self.name.lower() == "default"
|
|
181
238
|
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
239
|
+
def _set_values(
|
|
240
|
+
self,
|
|
241
|
+
project_id,
|
|
242
|
+
name,
|
|
243
|
+
description,
|
|
244
|
+
content_permissions,
|
|
245
|
+
parent_id,
|
|
246
|
+
owner_id,
|
|
247
|
+
top_level_project,
|
|
248
|
+
writeable,
|
|
249
|
+
project_count,
|
|
250
|
+
workbook_count,
|
|
251
|
+
view_count,
|
|
252
|
+
datasource_count,
|
|
253
|
+
owner,
|
|
254
|
+
):
|
|
198
255
|
if project_id is not None:
|
|
199
256
|
self._id = project_id
|
|
200
257
|
if name:
|
|
@@ -207,6 +264,20 @@ class ProjectItem:
|
|
|
207
264
|
self.parent_id = parent_id
|
|
208
265
|
if owner_id:
|
|
209
266
|
self._owner_id = owner_id
|
|
267
|
+
if project_count is not None:
|
|
268
|
+
self._project_count = project_count
|
|
269
|
+
if workbook_count is not None:
|
|
270
|
+
self._workbook_count = workbook_count
|
|
271
|
+
if view_count is not None:
|
|
272
|
+
self._view_count = view_count
|
|
273
|
+
if datasource_count is not None:
|
|
274
|
+
self._datasource_count = datasource_count
|
|
275
|
+
if top_level_project is not None:
|
|
276
|
+
self._top_level_project = top_level_project
|
|
277
|
+
if writeable is not None:
|
|
278
|
+
self._writeable = writeable
|
|
279
|
+
if owner is not None:
|
|
280
|
+
self._owner = owner
|
|
210
281
|
|
|
211
282
|
def _set_permissions(self, permissions):
|
|
212
283
|
self._permissions = permissions
|
|
@@ -220,31 +291,71 @@ class ProjectItem:
|
|
|
220
291
|
)
|
|
221
292
|
|
|
222
293
|
@classmethod
|
|
223
|
-
def from_response(cls, resp, ns) -> list["ProjectItem"]:
|
|
294
|
+
def from_response(cls, resp: bytes, ns: Optional[dict]) -> list["ProjectItem"]:
|
|
224
295
|
all_project_items = list()
|
|
225
296
|
parsed_response = fromstring(resp)
|
|
226
297
|
all_project_xml = parsed_response.findall(".//t:project", namespaces=ns)
|
|
227
298
|
|
|
228
299
|
for project_xml in all_project_xml:
|
|
229
|
-
project_item = cls.from_xml(project_xml)
|
|
300
|
+
project_item = cls.from_xml(project_xml, namespace=ns)
|
|
230
301
|
all_project_items.append(project_item)
|
|
231
302
|
return all_project_items
|
|
232
303
|
|
|
233
304
|
@classmethod
|
|
234
|
-
def from_xml(cls, project_xml, namespace=None) -> "ProjectItem":
|
|
305
|
+
def from_xml(cls, project_xml: ET.Element, namespace: Optional[dict] = None) -> "ProjectItem":
|
|
235
306
|
project_item = cls()
|
|
236
|
-
project_item._set_values(*cls._parse_element(project_xml))
|
|
307
|
+
project_item._set_values(*cls._parse_element(project_xml, namespace))
|
|
237
308
|
return project_item
|
|
238
309
|
|
|
239
310
|
@staticmethod
|
|
240
|
-
def _parse_element(project_xml):
|
|
311
|
+
def _parse_element(project_xml: ET.Element, namespace: Optional[dict]) -> tuple:
|
|
241
312
|
id = project_xml.get("id", None)
|
|
242
313
|
name = project_xml.get("name", None)
|
|
243
314
|
description = project_xml.get("description", None)
|
|
244
315
|
content_permissions = project_xml.get("contentPermissions", None)
|
|
245
316
|
parent_id = project_xml.get("parentProjectId", None)
|
|
317
|
+
top_level_project = str_to_bool(project_xml.get("topLevelProject", None))
|
|
318
|
+
writeable = str_to_bool(project_xml.get("writeable", None))
|
|
246
319
|
owner_id = None
|
|
247
|
-
|
|
248
|
-
|
|
320
|
+
owner = None
|
|
321
|
+
if (owner_elem := project_xml.find(".//t:owner", namespaces=namespace)) is not None:
|
|
322
|
+
owner = UserItem.from_xml(owner_elem, namespace)
|
|
323
|
+
owner_id = owner_elem.get("id", None)
|
|
324
|
+
|
|
325
|
+
project_count = None
|
|
326
|
+
workbook_count = None
|
|
327
|
+
view_count = None
|
|
328
|
+
datasource_count = None
|
|
329
|
+
if (count_elem := project_xml.find(".//t:contentsCounts", namespaces=namespace)) is not None:
|
|
330
|
+
project_count = int(count_elem.get("projectCount", 0))
|
|
331
|
+
workbook_count = int(count_elem.get("workbookCount", 0))
|
|
332
|
+
view_count = int(count_elem.get("viewCount", 0))
|
|
333
|
+
datasource_count = int(count_elem.get("dataSourceCount", 0))
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
id,
|
|
337
|
+
name,
|
|
338
|
+
description,
|
|
339
|
+
content_permissions,
|
|
340
|
+
parent_id,
|
|
341
|
+
owner_id,
|
|
342
|
+
top_level_project,
|
|
343
|
+
writeable,
|
|
344
|
+
project_count,
|
|
345
|
+
workbook_count,
|
|
346
|
+
view_count,
|
|
347
|
+
datasource_count,
|
|
348
|
+
owner,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@overload
|
|
353
|
+
def str_to_bool(value: str) -> bool: ...
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@overload
|
|
357
|
+
def str_to_bool(value: None) -> None: ...
|
|
358
|
+
|
|
249
359
|
|
|
250
|
-
|
|
360
|
+
def str_to_bool(value):
|
|
361
|
+
return value.lower() == "true" if value is not None else None
|
|
@@ -20,6 +20,63 @@ Interval = Union[HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval]
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class ScheduleItem:
|
|
23
|
+
"""
|
|
24
|
+
Using the TSC library, you can schedule extract refresh or subscription
|
|
25
|
+
tasks on Tableau Server. You can also get and update information about the
|
|
26
|
+
scheduled tasks, or delete scheduled tasks.
|
|
27
|
+
|
|
28
|
+
If you have the identifier of the job, you can use the TSC library to find
|
|
29
|
+
out the status of the asynchronous job.
|
|
30
|
+
|
|
31
|
+
The schedule properties are defined in the ScheduleItem class. The class
|
|
32
|
+
corresponds to the properties for schedules you can access in Tableau
|
|
33
|
+
Server or by using the Tableau Server REST API. The Schedule methods are
|
|
34
|
+
based upon the endpoints for jobs in the REST API and operate on the JobItem
|
|
35
|
+
class.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
name : str
|
|
40
|
+
The name of the schedule.
|
|
41
|
+
|
|
42
|
+
priority : int
|
|
43
|
+
The priority of the schedule. Lower values represent higher priority,
|
|
44
|
+
with 0 indicating the highest priority.
|
|
45
|
+
|
|
46
|
+
schedule_type : str
|
|
47
|
+
The type of task schedule. See ScheduleItem.Type for the possible values.
|
|
48
|
+
|
|
49
|
+
execution_order : str
|
|
50
|
+
Specifies how the scheduled tasks should run. The choices are Parallel
|
|
51
|
+
which uses all avaiable background processes for a scheduled task, or
|
|
52
|
+
Serial, which limits the schedule to one background process.
|
|
53
|
+
|
|
54
|
+
interval_item : Interval
|
|
55
|
+
Specifies the frequency that the scheduled task should run. The
|
|
56
|
+
interval_item is an instance of the IntervalItem class. The
|
|
57
|
+
interval_item has properties for frequency (hourly, daily, weekly,
|
|
58
|
+
monthly), and what time and date the scheduled item runs. You set this
|
|
59
|
+
value by declaring an IntervalItem object that is one of the following:
|
|
60
|
+
HourlyInterval, DailyInterval, WeeklyInterval, or MonthlyInterval.
|
|
61
|
+
|
|
62
|
+
Attributes
|
|
63
|
+
----------
|
|
64
|
+
created_at : datetime
|
|
65
|
+
The date and time the schedule was created.
|
|
66
|
+
|
|
67
|
+
end_schedule_at : datetime
|
|
68
|
+
The date and time the schedule ends.
|
|
69
|
+
|
|
70
|
+
id : str
|
|
71
|
+
The unique identifier for the schedule.
|
|
72
|
+
|
|
73
|
+
next_run_at : datetime
|
|
74
|
+
The date and time the schedule is next run.
|
|
75
|
+
|
|
76
|
+
state : str
|
|
77
|
+
The state of the schedule. See ScheduleItem.State for the possible values.
|
|
78
|
+
"""
|
|
79
|
+
|
|
23
80
|
class Type:
|
|
24
81
|
Extract = "Extract"
|
|
25
82
|
Flow = "Flow"
|
|
@@ -1188,6 +1188,34 @@ class SiteItem:
|
|
|
1188
1188
|
)
|
|
1189
1189
|
|
|
1190
1190
|
|
|
1191
|
+
class SiteAuthConfiguration:
|
|
1192
|
+
"""
|
|
1193
|
+
Authentication configuration for a site.
|
|
1194
|
+
"""
|
|
1195
|
+
|
|
1196
|
+
def __init__(self):
|
|
1197
|
+
self.auth_setting: Optional[str] = None
|
|
1198
|
+
self.enabled: Optional[bool] = None
|
|
1199
|
+
self.idp_configuration_id: Optional[str] = None
|
|
1200
|
+
self.idp_configuration_name: Optional[str] = None
|
|
1201
|
+
self.known_provider_alias: Optional[str] = None
|
|
1202
|
+
|
|
1203
|
+
@classmethod
|
|
1204
|
+
def from_response(cls, resp: bytes, ns: dict) -> list["SiteAuthConfiguration"]:
|
|
1205
|
+
all_auth_configs = list()
|
|
1206
|
+
parsed_response = fromstring(resp)
|
|
1207
|
+
all_auth_xml = parsed_response.findall(".//t:siteAuthConfiguration", namespaces=ns)
|
|
1208
|
+
for auth_xml in all_auth_xml:
|
|
1209
|
+
auth_config = cls()
|
|
1210
|
+
auth_config.auth_setting = auth_xml.get("authSetting", None)
|
|
1211
|
+
auth_config.enabled = string_to_bool(auth_xml.get("enabled", ""))
|
|
1212
|
+
auth_config.idp_configuration_id = auth_xml.get("idpConfigurationId", None)
|
|
1213
|
+
auth_config.idp_configuration_name = auth_xml.get("idpConfigurationName", None)
|
|
1214
|
+
auth_config.known_provider_alias = auth_xml.get("knownProviderAlias", None)
|
|
1215
|
+
all_auth_configs.append(auth_config)
|
|
1216
|
+
return all_auth_configs
|
|
1217
|
+
|
|
1218
|
+
|
|
1191
1219
|
# Used to convert string represented boolean to a boolean type
|
|
1192
1220
|
def string_to_bool(s: str) -> bool:
|
|
1193
1221
|
return s.lower() == "true"
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
from typing import Callable, Optional, TYPE_CHECKING
|
|
1
2
|
from defusedxml.ElementTree import fromstring
|
|
2
3
|
|
|
3
4
|
from .exceptions import UnpopulatedPropertyError
|
|
4
5
|
from .property_decorators import property_not_empty, property_is_boolean
|
|
5
6
|
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from tableauserverclient.models import DQWItem
|
|
9
|
+
|
|
6
10
|
|
|
7
11
|
class TableItem:
|
|
8
12
|
def __init__(self, name, description=None):
|
|
@@ -40,7 +44,7 @@ class TableItem:
|
|
|
40
44
|
return self._data_quality_warnings()
|
|
41
45
|
|
|
42
46
|
@property
|
|
43
|
-
def id(self):
|
|
47
|
+
def id(self) -> Optional[str]:
|
|
44
48
|
return self._id
|
|
45
49
|
|
|
46
50
|
@property
|
|
@@ -100,8 +104,8 @@ class TableItem:
|
|
|
100
104
|
def _set_columns(self, columns):
|
|
101
105
|
self._columns = columns
|
|
102
106
|
|
|
103
|
-
def _set_data_quality_warnings(self,
|
|
104
|
-
self._data_quality_warnings =
|
|
107
|
+
def _set_data_quality_warnings(self, dqw: Callable[[], list["DQWItem"]]) -> None:
|
|
108
|
+
self._data_quality_warnings = dqw
|
|
105
109
|
|
|
106
110
|
def _set_values(self, table_values):
|
|
107
111
|
if "id" in table_values:
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from typing import Union
|
|
2
2
|
|
|
3
|
+
from tableauserverclient.models.database_item import DatabaseItem
|
|
3
4
|
from tableauserverclient.models.datasource_item import DatasourceItem
|
|
4
5
|
from tableauserverclient.models.flow_item import FlowItem
|
|
5
6
|
from tableauserverclient.models.project_item import ProjectItem
|
|
7
|
+
from tableauserverclient.models.table_item import TableItem
|
|
6
8
|
from tableauserverclient.models.view_item import ViewItem
|
|
7
9
|
from tableauserverclient.models.workbook_item import WorkbookItem
|
|
8
10
|
from tableauserverclient.models.metric_item import MetricItem
|
|
@@ -25,7 +27,17 @@ class Resource:
|
|
|
25
27
|
|
|
26
28
|
# resource types that have permissions, can be renamed, etc
|
|
27
29
|
# todo: refactoring: should actually define TableauItem as an interface and let all these implement it
|
|
28
|
-
TableauItem = Union[
|
|
30
|
+
TableauItem = Union[
|
|
31
|
+
DatasourceItem,
|
|
32
|
+
FlowItem,
|
|
33
|
+
MetricItem,
|
|
34
|
+
ProjectItem,
|
|
35
|
+
ViewItem,
|
|
36
|
+
WorkbookItem,
|
|
37
|
+
VirtualConnectionItem,
|
|
38
|
+
DatabaseItem,
|
|
39
|
+
TableItem,
|
|
40
|
+
]
|
|
29
41
|
|
|
30
42
|
|
|
31
43
|
def plural_type(content_type: Union[Resource, str]) -> str:
|
|
@@ -7,6 +7,7 @@ from typing import Optional, TYPE_CHECKING
|
|
|
7
7
|
from defusedxml.ElementTree import fromstring
|
|
8
8
|
|
|
9
9
|
from tableauserverclient.datetime_helpers import parse_datetime
|
|
10
|
+
from tableauserverclient.models.site_item import SiteAuthConfiguration
|
|
10
11
|
from .exceptions import UnpopulatedPropertyError
|
|
11
12
|
from .property_decorators import (
|
|
12
13
|
property_is_enum,
|
|
@@ -37,6 +38,49 @@ class UserItem:
|
|
|
37
38
|
auth_setting: str
|
|
38
39
|
Required attribute for Tableau Cloud. How the user autenticates to the
|
|
39
40
|
server.
|
|
41
|
+
|
|
42
|
+
Attributes
|
|
43
|
+
----------
|
|
44
|
+
domain_name: Optional[str]
|
|
45
|
+
The name of the Active Directory domain ("local" if local authentication
|
|
46
|
+
is used).
|
|
47
|
+
|
|
48
|
+
email: Optional[str]
|
|
49
|
+
The email address of the user.
|
|
50
|
+
|
|
51
|
+
external_auth_user_id: Optional[str]
|
|
52
|
+
The unique identifier for the user in the external authentication system.
|
|
53
|
+
|
|
54
|
+
id: Optional[str]
|
|
55
|
+
The unique identifier for the user.
|
|
56
|
+
|
|
57
|
+
favorites: dict[str, list]
|
|
58
|
+
The favorites of the user. Must be populated with a call to
|
|
59
|
+
`populate_favorites()`.
|
|
60
|
+
|
|
61
|
+
fullname: Optional[str]
|
|
62
|
+
The full name of the user.
|
|
63
|
+
|
|
64
|
+
groups: Pager
|
|
65
|
+
The groups the user belongs to. Must be populated with a call to
|
|
66
|
+
`populate_groups()`.
|
|
67
|
+
|
|
68
|
+
last_login: Optional[datetime]
|
|
69
|
+
The last time the user logged in.
|
|
70
|
+
|
|
71
|
+
locale: Optional[str]
|
|
72
|
+
The locale of the user.
|
|
73
|
+
|
|
74
|
+
language: Optional[str]
|
|
75
|
+
Language setting for the user.
|
|
76
|
+
|
|
77
|
+
idp_configuration_id: Optional[str]
|
|
78
|
+
The ID of the identity provider configuration.
|
|
79
|
+
|
|
80
|
+
workbooks: Pager
|
|
81
|
+
The workbooks owned by the user. Must be populated with a call to
|
|
82
|
+
`populate_workbooks()`.
|
|
83
|
+
|
|
40
84
|
"""
|
|
41
85
|
|
|
42
86
|
tag_name: str = "user"
|
|
@@ -94,6 +138,9 @@ class UserItem:
|
|
|
94
138
|
self.name: Optional[str] = name
|
|
95
139
|
self.site_role: Optional[str] = site_role
|
|
96
140
|
self.auth_setting: Optional[str] = auth_setting
|
|
141
|
+
self._locale: Optional[str] = None
|
|
142
|
+
self._language: Optional[str] = None
|
|
143
|
+
self._idp_configuration_id: Optional[str] = None
|
|
97
144
|
|
|
98
145
|
return None
|
|
99
146
|
|
|
@@ -184,6 +231,26 @@ class UserItem:
|
|
|
184
231
|
raise UnpopulatedPropertyError(error)
|
|
185
232
|
return self._groups()
|
|
186
233
|
|
|
234
|
+
@property
|
|
235
|
+
def locale(self) -> Optional[str]:
|
|
236
|
+
return self._locale
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def language(self) -> Optional[str]:
|
|
240
|
+
return self._language
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def idp_configuration_id(self) -> Optional[str]:
|
|
244
|
+
"""
|
|
245
|
+
IDP configuration id for the user. This is only available on Tableau
|
|
246
|
+
Cloud, 3.24 or later
|
|
247
|
+
"""
|
|
248
|
+
return self._idp_configuration_id
|
|
249
|
+
|
|
250
|
+
@idp_configuration_id.setter
|
|
251
|
+
def idp_configuration_id(self, value: str) -> None:
|
|
252
|
+
self._idp_configuration_id = value
|
|
253
|
+
|
|
187
254
|
def _set_workbooks(self, workbooks) -> None:
|
|
188
255
|
self._workbooks = workbooks
|
|
189
256
|
|
|
@@ -204,8 +271,11 @@ class UserItem:
|
|
|
204
271
|
email,
|
|
205
272
|
auth_setting,
|
|
206
273
|
_,
|
|
274
|
+
_,
|
|
275
|
+
_,
|
|
276
|
+
_,
|
|
207
277
|
) = self._parse_element(user_xml, ns)
|
|
208
|
-
self._set_values(None, None, site_role, None, None, fullname, email, auth_setting, None)
|
|
278
|
+
self._set_values(None, None, site_role, None, None, fullname, email, auth_setting, None, None, None, None)
|
|
209
279
|
return self
|
|
210
280
|
|
|
211
281
|
def _set_values(
|
|
@@ -219,6 +289,9 @@ class UserItem:
|
|
|
219
289
|
email,
|
|
220
290
|
auth_setting,
|
|
221
291
|
domain_name,
|
|
292
|
+
locale,
|
|
293
|
+
language,
|
|
294
|
+
idp_configuration_id,
|
|
222
295
|
):
|
|
223
296
|
if id is not None:
|
|
224
297
|
self._id = id
|
|
@@ -238,6 +311,12 @@ class UserItem:
|
|
|
238
311
|
self._auth_setting = auth_setting
|
|
239
312
|
if domain_name:
|
|
240
313
|
self._domain_name = domain_name
|
|
314
|
+
if locale:
|
|
315
|
+
self._locale = locale
|
|
316
|
+
if language:
|
|
317
|
+
self._language = language
|
|
318
|
+
if idp_configuration_id:
|
|
319
|
+
self._idp_configuration_id = idp_configuration_id
|
|
241
320
|
|
|
242
321
|
@classmethod
|
|
243
322
|
def from_response(cls, resp, ns) -> list["UserItem"]:
|
|
@@ -249,6 +328,12 @@ class UserItem:
|
|
|
249
328
|
element_name = ".//t:owner"
|
|
250
329
|
return cls._parse_xml(element_name, resp, ns)
|
|
251
330
|
|
|
331
|
+
@classmethod
|
|
332
|
+
def from_xml(cls, xml: ET.Element, ns: Optional[dict] = None) -> "UserItem":
|
|
333
|
+
item = cls()
|
|
334
|
+
item._set_values(*cls._parse_element(xml, ns))
|
|
335
|
+
return item
|
|
336
|
+
|
|
252
337
|
@classmethod
|
|
253
338
|
def _parse_xml(cls, element_name, resp, ns):
|
|
254
339
|
all_user_items = []
|
|
@@ -265,6 +350,9 @@ class UserItem:
|
|
|
265
350
|
email,
|
|
266
351
|
auth_setting,
|
|
267
352
|
domain_name,
|
|
353
|
+
locale,
|
|
354
|
+
language,
|
|
355
|
+
idp_configuration_id,
|
|
268
356
|
) = cls._parse_element(user_xml, ns)
|
|
269
357
|
user_item = cls(name, site_role)
|
|
270
358
|
user_item._set_values(
|
|
@@ -277,6 +365,9 @@ class UserItem:
|
|
|
277
365
|
email,
|
|
278
366
|
auth_setting,
|
|
279
367
|
domain_name,
|
|
368
|
+
locale,
|
|
369
|
+
language,
|
|
370
|
+
idp_configuration_id,
|
|
280
371
|
)
|
|
281
372
|
all_user_items.append(user_item)
|
|
282
373
|
return all_user_items
|
|
@@ -295,6 +386,9 @@ class UserItem:
|
|
|
295
386
|
fullname = user_xml.get("fullName", None)
|
|
296
387
|
email = user_xml.get("email", None)
|
|
297
388
|
auth_setting = user_xml.get("authSetting", None)
|
|
389
|
+
locale = user_xml.get("locale", None)
|
|
390
|
+
language = user_xml.get("language", None)
|
|
391
|
+
idp_configuration_id = user_xml.get("idpConfigurationId", None)
|
|
298
392
|
|
|
299
393
|
domain_name = None
|
|
300
394
|
domain_elem = user_xml.find(".//t:domain", namespaces=ns)
|
|
@@ -311,6 +405,9 @@ class UserItem:
|
|
|
311
405
|
email,
|
|
312
406
|
auth_setting,
|
|
313
407
|
domain_name,
|
|
408
|
+
locale,
|
|
409
|
+
language,
|
|
410
|
+
idp_configuration_id,
|
|
314
411
|
)
|
|
315
412
|
|
|
316
413
|
class CSVImport:
|
|
@@ -361,6 +458,9 @@ class UserItem:
|
|
|
361
458
|
values[UserItem.CSVImport.ColumnType.EMAIL],
|
|
362
459
|
values[UserItem.CSVImport.ColumnType.AUTH],
|
|
363
460
|
None,
|
|
461
|
+
None,
|
|
462
|
+
None,
|
|
463
|
+
None,
|
|
364
464
|
)
|
|
365
465
|
return user
|
|
366
466
|
|