tableauserverclient 0.37__py3-none-any.whl → 0.39__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. tableauserverclient/bin/_version.py → _version.py +3 -3
  2. bin/__init__.py +3 -0
  3. bin/_version.py +21 -0
  4. {tableauserverclient/helpers → helpers}/strings.py +25 -1
  5. {tableauserverclient/models → models}/__init__.py +15 -1
  6. models/collection_item.py +52 -0
  7. {tableauserverclient/models → models}/connection_item.py +16 -2
  8. {tableauserverclient/models → models}/custom_view_item.py +8 -0
  9. {tableauserverclient/models → models}/data_freshness_policy_item.py +3 -3
  10. {tableauserverclient/models → models}/datasource_item.py +113 -3
  11. models/extensions_item.py +186 -0
  12. models/extract_item.py +82 -0
  13. {tableauserverclient/models → models}/favorites_item.py +21 -8
  14. {tableauserverclient/models → models}/flow_item.py +3 -3
  15. {tableauserverclient/models → models}/group_item.py +18 -1
  16. {tableauserverclient/models → models}/groupset_item.py +14 -0
  17. {tableauserverclient/models → models}/interval_item.py +42 -1
  18. models/location_item.py +53 -0
  19. models/oidc_item.py +82 -0
  20. {tableauserverclient/models → models}/permissions_item.py +2 -0
  21. {tableauserverclient/models → models}/project_item.py +141 -29
  22. {tableauserverclient/models → models}/property_decorators.py +2 -2
  23. {tableauserverclient/models → models}/reference_item.py +12 -6
  24. {tableauserverclient/models → models}/schedule_item.py +67 -1
  25. {tableauserverclient/models → models}/site_item.py +54 -0
  26. {tableauserverclient/models → models}/table_item.py +7 -3
  27. {tableauserverclient/models → models}/tableau_auth.py +13 -6
  28. {tableauserverclient/models → models}/tableau_types.py +13 -1
  29. {tableauserverclient/models → models}/user_item.py +111 -4
  30. {tableauserverclient/models → models}/view_item.py +79 -5
  31. {tableauserverclient/models → models}/workbook_item.py +153 -3
  32. {tableauserverclient/server → server}/endpoint/__init__.py +4 -0
  33. {tableauserverclient/server → server}/endpoint/databases_endpoint.py +101 -18
  34. {tableauserverclient/server → server}/endpoint/datasources_endpoint.py +155 -25
  35. {tableauserverclient/server → server}/endpoint/dqw_endpoint.py +16 -6
  36. {tableauserverclient/server → server}/endpoint/endpoint.py +39 -0
  37. server/endpoint/extensions_endpoint.py +79 -0
  38. {tableauserverclient/server → server}/endpoint/flow_task_endpoint.py +1 -1
  39. {tableauserverclient/server → server}/endpoint/flows_endpoint.py +5 -4
  40. server/endpoint/oidc_endpoint.py +157 -0
  41. {tableauserverclient/server → server}/endpoint/projects_endpoint.py +12 -0
  42. server/endpoint/schedules_endpoint.py +328 -0
  43. {tableauserverclient/server → server}/endpoint/sites_endpoint.py +18 -1
  44. {tableauserverclient/server → server}/endpoint/tables_endpoint.py +140 -17
  45. {tableauserverclient/server → server}/endpoint/users_endpoint.py +296 -10
  46. {tableauserverclient/server → server}/endpoint/views_endpoint.py +23 -0
  47. {tableauserverclient/server → server}/endpoint/workbooks_endpoint.py +124 -9
  48. {tableauserverclient/server → server}/query.py +36 -0
  49. {tableauserverclient/server → server}/request_factory.py +286 -2
  50. {tableauserverclient/server → server}/request_options.py +139 -3
  51. {tableauserverclient/server → server}/server.py +46 -0
  52. {tableauserverclient-0.37.dist-info → tableauserverclient-0.39.dist-info}/METADATA +5 -26
  53. tableauserverclient-0.39.dist-info/RECORD +107 -0
  54. {tableauserverclient-0.37.dist-info → tableauserverclient-0.39.dist-info}/WHEEL +1 -1
  55. tableauserverclient-0.39.dist-info/top_level.txt +4 -0
  56. tableauserverclient/__init__.py +0 -141
  57. tableauserverclient/config.py +0 -27
  58. tableauserverclient/datetime_helpers.py +0 -45
  59. tableauserverclient/exponential_backoff.py +0 -30
  60. tableauserverclient/filesys_helpers.py +0 -63
  61. tableauserverclient/namespace.py +0 -37
  62. tableauserverclient/py.typed +0 -0
  63. tableauserverclient/server/endpoint/schedules_endpoint.py +0 -151
  64. tableauserverclient-0.37.dist-info/RECORD +0 -106
  65. tableauserverclient-0.37.dist-info/licenses/LICENSE.versioneer +0 -7
  66. tableauserverclient-0.37.dist-info/top_level.txt +0 -1
  67. {tableauserverclient/helpers → helpers}/__init__.py +0 -0
  68. {tableauserverclient/helpers → helpers}/headers.py +0 -0
  69. {tableauserverclient/helpers → helpers}/logging.py +0 -0
  70. {tableauserverclient/models → models}/column_item.py +0 -0
  71. {tableauserverclient/models → models}/connection_credentials.py +0 -0
  72. {tableauserverclient/models → models}/data_acceleration_report_item.py +0 -0
  73. {tableauserverclient/models → models}/data_alert_item.py +0 -0
  74. {tableauserverclient/models → models}/database_item.py +0 -0
  75. {tableauserverclient/models → models}/dqw_item.py +0 -0
  76. {tableauserverclient/models → models}/exceptions.py +0 -0
  77. {tableauserverclient/models → models}/fileupload_item.py +0 -0
  78. {tableauserverclient/models → models}/flow_run_item.py +0 -0
  79. {tableauserverclient/models → models}/job_item.py +0 -0
  80. {tableauserverclient/models → models}/linked_tasks_item.py +0 -0
  81. {tableauserverclient/models → models}/metric_item.py +0 -0
  82. {tableauserverclient/models → models}/pagination_item.py +0 -0
  83. {tableauserverclient/models → models}/revision_item.py +0 -0
  84. {tableauserverclient/models → models}/server_info_item.py +0 -0
  85. {tableauserverclient/models → models}/subscription_item.py +0 -0
  86. {tableauserverclient/models → models}/tag_item.py +0 -0
  87. {tableauserverclient/models → models}/target.py +0 -0
  88. {tableauserverclient/models → models}/task_item.py +0 -0
  89. {tableauserverclient/models → models}/virtual_connection_item.py +0 -0
  90. {tableauserverclient/models → models}/webhook_item.py +0 -0
  91. {tableauserverclient/server → server}/__init__.py +0 -0
  92. {tableauserverclient/server → server}/endpoint/auth_endpoint.py +0 -0
  93. {tableauserverclient/server → server}/endpoint/custom_views_endpoint.py +0 -0
  94. {tableauserverclient/server → server}/endpoint/data_acceleration_report_endpoint.py +0 -0
  95. {tableauserverclient/server → server}/endpoint/data_alert_endpoint.py +0 -0
  96. {tableauserverclient/server → server}/endpoint/default_permissions_endpoint.py +0 -0
  97. {tableauserverclient/server → server}/endpoint/exceptions.py +0 -0
  98. {tableauserverclient/server → server}/endpoint/favorites_endpoint.py +0 -0
  99. {tableauserverclient/server → server}/endpoint/fileuploads_endpoint.py +0 -0
  100. {tableauserverclient/server → server}/endpoint/flow_runs_endpoint.py +0 -0
  101. {tableauserverclient/server → server}/endpoint/groups_endpoint.py +0 -0
  102. {tableauserverclient/server → server}/endpoint/groupsets_endpoint.py +0 -0
  103. {tableauserverclient/server → server}/endpoint/jobs_endpoint.py +0 -0
  104. {tableauserverclient/server → server}/endpoint/linked_tasks_endpoint.py +0 -0
  105. {tableauserverclient/server → server}/endpoint/metadata_endpoint.py +0 -0
  106. {tableauserverclient/server → server}/endpoint/metrics_endpoint.py +0 -0
  107. {tableauserverclient/server → server}/endpoint/permissions_endpoint.py +0 -0
  108. {tableauserverclient/server → server}/endpoint/resource_tagger.py +0 -0
  109. {tableauserverclient/server → server}/endpoint/server_info_endpoint.py +0 -0
  110. {tableauserverclient/server → server}/endpoint/subscriptions_endpoint.py +0 -0
  111. {tableauserverclient/server → server}/endpoint/tasks_endpoint.py +0 -0
  112. {tableauserverclient/server → server}/endpoint/virtual_connections_endpoint.py +0 -0
  113. {tableauserverclient/server → server}/endpoint/webhooks_endpoint.py +0 -0
  114. {tableauserverclient/server → server}/exceptions.py +0 -0
  115. {tableauserverclient/server → server}/filter.py +0 -0
  116. {tableauserverclient/server → server}/pager.py +0 -0
  117. {tableauserverclient/server → server}/sort.py +0 -0
  118. {tableauserverclient-0.37.dist-info → tableauserverclient-0.39.dist-info}/licenses/LICENSE +0 -0
@@ -5,8 +5,10 @@ from enum import IntEnum
5
5
  from typing import Optional, TYPE_CHECKING
6
6
 
7
7
  from defusedxml.ElementTree import fromstring
8
+ from typing_extensions import Self
8
9
 
9
10
  from tableauserverclient.datetime_helpers import parse_datetime
11
+ from tableauserverclient.models.site_item import SiteAuthConfiguration
10
12
  from .exceptions import UnpopulatedPropertyError
11
13
  from .property_decorators import (
12
14
  property_is_enum,
@@ -16,6 +18,7 @@ from .reference_item import ResourceReference
16
18
 
17
19
  if TYPE_CHECKING:
18
20
  from tableauserverclient.server import Pager
21
+ from tableauserverclient.models.favorites_item import FavoriteType
19
22
 
20
23
 
21
24
  class UserItem:
@@ -37,6 +40,49 @@ class UserItem:
37
40
  auth_setting: str
38
41
  Required attribute for Tableau Cloud. How the user autenticates to the
39
42
  server.
43
+
44
+ Attributes
45
+ ----------
46
+ domain_name: Optional[str]
47
+ The name of the Active Directory domain ("local" if local authentication
48
+ is used).
49
+
50
+ email: Optional[str]
51
+ The email address of the user.
52
+
53
+ external_auth_user_id: Optional[str]
54
+ The unique identifier for the user in the external authentication system.
55
+
56
+ id: Optional[str]
57
+ The unique identifier for the user.
58
+
59
+ favorites: dict[str, list]
60
+ The favorites of the user. Must be populated with a call to
61
+ `populate_favorites()`.
62
+
63
+ fullname: Optional[str]
64
+ The full name of the user.
65
+
66
+ groups: Pager
67
+ The groups the user belongs to. Must be populated with a call to
68
+ `populate_groups()`.
69
+
70
+ last_login: Optional[datetime]
71
+ The last time the user logged in.
72
+
73
+ locale: Optional[str]
74
+ The locale of the user.
75
+
76
+ language: Optional[str]
77
+ Language setting for the user.
78
+
79
+ idp_configuration_id: Optional[str]
80
+ The ID of the identity provider configuration.
81
+
82
+ workbooks: Pager
83
+ The workbooks owned by the user. Must be populated with a call to
84
+ `populate_workbooks()`.
85
+
40
86
  """
41
87
 
42
88
  tag_name: str = "user"
@@ -87,13 +133,16 @@ class UserItem:
87
133
  self._id: Optional[str] = None
88
134
  self._last_login: Optional[datetime] = None
89
135
  self._workbooks = None
90
- self._favorites: Optional[dict[str, list]] = None
136
+ self._favorites: Optional["FavoriteType"] = None
91
137
  self._groups = None
92
138
  self.email: Optional[str] = None
93
139
  self.fullname: Optional[str] = None
94
140
  self.name: Optional[str] = name
95
141
  self.site_role: Optional[str] = site_role
96
142
  self.auth_setting: Optional[str] = auth_setting
143
+ self._locale: Optional[str] = None
144
+ self._language: Optional[str] = None
145
+ self._idp_configuration_id: Optional[str] = None
97
146
 
98
147
  return None
99
148
 
@@ -138,7 +187,7 @@ class UserItem:
138
187
  return self._name
139
188
 
140
189
  @name.setter
141
- def name(self, value: str):
190
+ def name(self, value: Optional[str]):
142
191
  self._name = value
143
192
 
144
193
  # valid: username, domain/username, username@domain, domain/username@email
@@ -171,7 +220,7 @@ class UserItem:
171
220
  return self._workbooks()
172
221
 
173
222
  @property
174
- def favorites(self) -> dict[str, list]:
223
+ def favorites(self) -> "FavoriteType":
175
224
  if self._favorites is None:
176
225
  error = "User item must be populated with favorites first."
177
226
  raise UnpopulatedPropertyError(error)
@@ -184,6 +233,26 @@ class UserItem:
184
233
  raise UnpopulatedPropertyError(error)
185
234
  return self._groups()
186
235
 
236
+ @property
237
+ def locale(self) -> Optional[str]:
238
+ return self._locale
239
+
240
+ @property
241
+ def language(self) -> Optional[str]:
242
+ return self._language
243
+
244
+ @property
245
+ def idp_configuration_id(self) -> Optional[str]:
246
+ """
247
+ IDP configuration id for the user. This is only available on Tableau
248
+ Cloud, 3.24 or later
249
+ """
250
+ return self._idp_configuration_id
251
+
252
+ @idp_configuration_id.setter
253
+ def idp_configuration_id(self, value: str) -> None:
254
+ self._idp_configuration_id = value
255
+
187
256
  def _set_workbooks(self, workbooks) -> None:
188
257
  self._workbooks = workbooks
189
258
 
@@ -204,8 +273,11 @@ class UserItem:
204
273
  email,
205
274
  auth_setting,
206
275
  _,
276
+ _,
277
+ _,
278
+ _,
207
279
  ) = self._parse_element(user_xml, ns)
208
- self._set_values(None, None, site_role, None, None, fullname, email, auth_setting, None)
280
+ self._set_values(None, None, site_role, None, None, fullname, email, auth_setting, None, None, None, None)
209
281
  return self
210
282
 
211
283
  def _set_values(
@@ -219,6 +291,9 @@ class UserItem:
219
291
  email,
220
292
  auth_setting,
221
293
  domain_name,
294
+ locale,
295
+ language,
296
+ idp_configuration_id,
222
297
  ):
223
298
  if id is not None:
224
299
  self._id = id
@@ -238,6 +313,12 @@ class UserItem:
238
313
  self._auth_setting = auth_setting
239
314
  if domain_name:
240
315
  self._domain_name = domain_name
316
+ if locale:
317
+ self._locale = locale
318
+ if language:
319
+ self._language = language
320
+ if idp_configuration_id:
321
+ self._idp_configuration_id = idp_configuration_id
241
322
 
242
323
  @classmethod
243
324
  def from_response(cls, resp, ns) -> list["UserItem"]:
@@ -249,6 +330,12 @@ class UserItem:
249
330
  element_name = ".//t:owner"
250
331
  return cls._parse_xml(element_name, resp, ns)
251
332
 
333
+ @classmethod
334
+ def from_xml(cls, xml: ET.Element, ns: Optional[dict] = None) -> "UserItem":
335
+ item = cls()
336
+ item._set_values(*cls._parse_element(xml, ns))
337
+ return item
338
+
252
339
  @classmethod
253
340
  def _parse_xml(cls, element_name, resp, ns):
254
341
  all_user_items = []
@@ -265,6 +352,9 @@ class UserItem:
265
352
  email,
266
353
  auth_setting,
267
354
  domain_name,
355
+ locale,
356
+ language,
357
+ idp_configuration_id,
268
358
  ) = cls._parse_element(user_xml, ns)
269
359
  user_item = cls(name, site_role)
270
360
  user_item._set_values(
@@ -277,6 +367,9 @@ class UserItem:
277
367
  email,
278
368
  auth_setting,
279
369
  domain_name,
370
+ locale,
371
+ language,
372
+ idp_configuration_id,
280
373
  )
281
374
  all_user_items.append(user_item)
282
375
  return all_user_items
@@ -285,6 +378,11 @@ class UserItem:
285
378
  def as_reference(id_) -> ResourceReference:
286
379
  return ResourceReference(id_, UserItem.tag_name)
287
380
 
381
+ def to_reference(self: Self) -> ResourceReference:
382
+ if self.id is None:
383
+ raise ValueError(f"{self.__class__.__qualname__} must have id to be converted to reference")
384
+ return ResourceReference(self.id, self.tag_name)
385
+
288
386
  @staticmethod
289
387
  def _parse_element(user_xml, ns):
290
388
  id = user_xml.get("id", None)
@@ -295,6 +393,9 @@ class UserItem:
295
393
  fullname = user_xml.get("fullName", None)
296
394
  email = user_xml.get("email", None)
297
395
  auth_setting = user_xml.get("authSetting", None)
396
+ locale = user_xml.get("locale", None)
397
+ language = user_xml.get("language", None)
398
+ idp_configuration_id = user_xml.get("idpConfigurationId", None)
298
399
 
299
400
  domain_name = None
300
401
  domain_elem = user_xml.find(".//t:domain", namespaces=ns)
@@ -311,6 +412,9 @@ class UserItem:
311
412
  email,
312
413
  auth_setting,
313
414
  domain_name,
415
+ locale,
416
+ language,
417
+ idp_configuration_id,
314
418
  )
315
419
 
316
420
  class CSVImport:
@@ -361,6 +465,9 @@ class UserItem:
361
465
  values[UserItem.CSVImport.ColumnType.EMAIL],
362
466
  values[UserItem.CSVImport.ColumnType.AUTH],
363
467
  None,
468
+ None,
469
+ None,
470
+ None,
364
471
  )
365
472
  return user
366
473
 
@@ -1,15 +1,21 @@
1
1
  import copy
2
2
  from datetime import datetime
3
3
  from requests import Response
4
- from typing import Callable, Optional
4
+ from typing import TYPE_CHECKING, Callable, Optional, overload
5
5
  from collections.abc import Iterator
6
6
 
7
7
  from defusedxml.ElementTree import fromstring
8
8
 
9
9
  from tableauserverclient.datetime_helpers import parse_datetime
10
10
  from tableauserverclient.models.exceptions import UnpopulatedPropertyError
11
+ from tableauserverclient.models.location_item import LocationItem
11
12
  from tableauserverclient.models.permissions_item import PermissionsRule
13
+ from tableauserverclient.models.project_item import ProjectItem
12
14
  from tableauserverclient.models.tag_item import TagItem
15
+ from tableauserverclient.models.user_item import UserItem
16
+
17
+ if TYPE_CHECKING:
18
+ from tableauserverclient.models.workbook_item import WorkbookItem
13
19
 
14
20
 
15
21
  class ViewItem:
@@ -34,9 +40,16 @@ class ViewItem:
34
40
  The image of the view. You must first call the `views.populate_image`
35
41
  method to access the image.
36
42
 
43
+ location: Optional[LocationItem], default None
44
+ The location of the view. The location can be a personal space or a
45
+ project.
46
+
37
47
  name: Optional[str], default None
38
48
  The name of the view.
39
49
 
50
+ owner: Optional[UserItem], default None
51
+ The owner of the view.
52
+
40
53
  owner_id: Optional[str], default None
41
54
  The ID for the owner of the view.
42
55
 
@@ -48,6 +61,9 @@ class ViewItem:
48
61
  The preview image of the view. You must first call the
49
62
  `views.populate_preview_image` method to access the preview image.
50
63
 
64
+ project: Optional[ProjectItem], default None
65
+ The project that contains the view.
66
+
51
67
  project_id: Optional[str], default None
52
68
  The ID for the project that contains the view.
53
69
 
@@ -60,9 +76,11 @@ class ViewItem:
60
76
  updated_at: Optional[datetime], default None
61
77
  The date and time when the view was last updated.
62
78
 
79
+ workbook: Optional[WorkbookItem], default None
80
+ The workbook that contains the view.
81
+
63
82
  workbook_id: Optional[str], default None
64
83
  The ID for the workbook that contains the view.
65
-
66
84
  """
67
85
 
68
86
  def __init__(self) -> None:
@@ -84,11 +102,18 @@ class ViewItem:
84
102
  self._workbook_id: Optional[str] = None
85
103
  self._permissions: Optional[Callable[[], list[PermissionsRule]]] = None
86
104
  self.tags: set[str] = set()
105
+ self._favorites_total: Optional[int] = None
106
+ self._view_url_name: Optional[str] = None
87
107
  self._data_acceleration_config = {
88
108
  "acceleration_enabled": None,
89
109
  "acceleration_status": None,
90
110
  }
91
111
 
112
+ self._owner: Optional[UserItem] = None
113
+ self._project: Optional[ProjectItem] = None
114
+ self._workbook: Optional["WorkbookItem"] = None
115
+ self._location: Optional[LocationItem] = None
116
+
92
117
  def __str__(self):
93
118
  return "<ViewItem {} '{}' contentUrl='{}' project={}>".format(
94
119
  self._id, self.name, self.content_url, self.project_id
@@ -190,6 +215,14 @@ class ViewItem:
190
215
  def workbook_id(self) -> Optional[str]:
191
216
  return self._workbook_id
192
217
 
218
+ @property
219
+ def view_url_name(self) -> Optional[str]:
220
+ return self._view_url_name
221
+
222
+ @property
223
+ def favorites_total(self) -> Optional[int]:
224
+ return self._favorites_total
225
+
193
226
  @property
194
227
  def data_acceleration_config(self):
195
228
  return self._data_acceleration_config
@@ -198,6 +231,22 @@ class ViewItem:
198
231
  def data_acceleration_config(self, value):
199
232
  self._data_acceleration_config = value
200
233
 
234
+ @property
235
+ def project(self) -> Optional["ProjectItem"]:
236
+ return self._project
237
+
238
+ @property
239
+ def workbook(self) -> Optional["WorkbookItem"]:
240
+ return self._workbook
241
+
242
+ @property
243
+ def owner(self) -> Optional[UserItem]:
244
+ return self._owner
245
+
246
+ @property
247
+ def location(self) -> Optional[LocationItem]:
248
+ return self._location
249
+
201
250
  @property
202
251
  def permissions(self) -> list[PermissionsRule]:
203
252
  if self._permissions is None:
@@ -228,7 +277,7 @@ class ViewItem:
228
277
  workbook_elem = view_xml.find(".//t:workbook", namespaces=ns)
229
278
  owner_elem = view_xml.find(".//t:owner", namespaces=ns)
230
279
  project_elem = view_xml.find(".//t:project", namespaces=ns)
231
- tags_elem = view_xml.find(".//t:tags", namespaces=ns)
280
+ tags_elem = view_xml.find("./t:tags", namespaces=ns)
232
281
  data_acceleration_config_elem = view_xml.find(".//t:dataAccelerationConfig", namespaces=ns)
233
282
  view_item._created_at = parse_datetime(view_xml.get("createdAt", None))
234
283
  view_item._updated_at = parse_datetime(view_xml.get("updatedAt", None))
@@ -236,22 +285,35 @@ class ViewItem:
236
285
  view_item._name = view_xml.get("name", None)
237
286
  view_item._content_url = view_xml.get("contentUrl", None)
238
287
  view_item._sheet_type = view_xml.get("sheetType", None)
288
+ view_item._favorites_total = string_to_int(view_xml.get("favoritesTotal", None))
289
+ view_item._view_url_name = view_xml.get("viewUrlName", None)
239
290
  if usage_elem is not None:
240
291
  total_view = usage_elem.get("totalViewCount", None)
241
292
  if total_view:
242
293
  view_item._total_views = int(total_view)
243
294
  if owner_elem is not None:
295
+ user = UserItem.from_xml(owner_elem, ns)
296
+ view_item._owner = user
244
297
  view_item._owner_id = owner_elem.get("id", None)
245
298
  if project_elem is not None:
246
- view_item._project_id = project_elem.get("id", None)
299
+ project_item = ProjectItem.from_xml(project_elem, ns)
300
+ view_item._project = project_item
301
+ view_item._project_id = project_item.id
247
302
  if workbook_id:
248
303
  view_item._workbook_id = workbook_id
249
304
  elif workbook_elem is not None:
250
- view_item._workbook_id = workbook_elem.get("id", None)
305
+ from tableauserverclient.models.workbook_item import WorkbookItem
306
+
307
+ workbook_item = WorkbookItem.from_xml(workbook_elem, ns)
308
+ view_item._workbook = workbook_item
309
+ view_item._workbook_id = workbook_item.id
251
310
  if tags_elem is not None:
252
311
  tags = TagItem.from_xml_element(tags_elem, ns)
253
312
  view_item.tags = tags
254
313
  view_item._initial_tags = copy.copy(tags)
314
+ if (location_elem := view_xml.find(".//t:location", namespaces=ns)) is not None:
315
+ location = LocationItem.from_xml(location_elem, ns)
316
+ view_item._location = location
255
317
  if data_acceleration_config_elem is not None:
256
318
  data_acceleration_config = parse_data_acceleration_config(data_acceleration_config_elem)
257
319
  view_item.data_acceleration_config = data_acceleration_config
@@ -274,3 +336,15 @@ def parse_data_acceleration_config(data_acceleration_elem):
274
336
 
275
337
  def string_to_bool(s: str) -> bool:
276
338
  return s.lower() == "true"
339
+
340
+
341
+ @overload
342
+ def string_to_int(s: None) -> None: ...
343
+
344
+
345
+ @overload
346
+ def string_to_int(s: str) -> int: ...
347
+
348
+
349
+ def string_to_int(s):
350
+ return int(s) if s is not None else None
@@ -2,11 +2,14 @@ import copy
2
2
  import datetime
3
3
  import uuid
4
4
  import xml.etree.ElementTree as ET
5
- from typing import Callable, Optional
5
+ from typing import Callable, Optional, overload
6
6
 
7
7
  from defusedxml.ElementTree import fromstring
8
8
 
9
9
  from tableauserverclient.datetime_helpers import parse_datetime
10
+ from tableauserverclient.models.location_item import LocationItem
11
+ from tableauserverclient.models.project_item import ProjectItem
12
+ from tableauserverclient.models.user_item import UserItem
10
13
  from .connection_item import ConnectionItem
11
14
  from .exceptions import UnpopulatedPropertyError
12
15
  from .permissions_item import PermissionsRule
@@ -51,13 +54,31 @@ class WorkbookItem:
51
54
  created_at : Optional[datetime.datetime]
52
55
  The date and time the workbook was created.
53
56
 
57
+ default_view_id : Optional[str]
58
+ The identifier for the default view of the workbook.
59
+
54
60
  description : Optional[str]
55
61
  User-defined description of the workbook.
56
62
 
63
+ encrypt_extracts : Optional[bool]
64
+ Indicates whether extracts are encrypted.
65
+
66
+ has_extracts : Optional[bool]
67
+ Indicates whether the workbook has extracts.
68
+
57
69
  id : Optional[str]
58
70
  The identifier for the workbook. You need this value to query a specific
59
71
  workbook or to delete a workbook with the get_by_id and delete methods.
60
72
 
73
+ last_published_at : Optional[datetime.datetime]
74
+ The date and time the workbook was last published.
75
+
76
+ location : Optional[LocationItem]
77
+ The location of the workbook, such as a personal space or project.
78
+
79
+ owner : Optional[UserItem]
80
+ The owner of the workbook.
81
+
61
82
  owner_id : Optional[str]
62
83
  The identifier for the owner (UserItem) of the workbook.
63
84
 
@@ -65,6 +86,9 @@ class WorkbookItem:
65
86
  The thumbnail image for the view. You must first call the
66
87
  workbooks.populate_preview_image method to access this data.
67
88
 
89
+ project: Optional[ProjectItem]
90
+ The project that contains the workbook.
91
+
68
92
  project_name : Optional[str]
69
93
  The name of the project that contains the workbook.
70
94
 
@@ -139,6 +163,15 @@ class WorkbookItem:
139
163
  self._permissions = None
140
164
  self.thumbnails_user_id = thumbnails_user_id
141
165
  self.thumbnails_group_id = thumbnails_group_id
166
+ self._sheet_count: Optional[int] = None
167
+ self._has_extracts: Optional[bool] = None
168
+ self._project: Optional[ProjectItem] = None
169
+ self._owner: Optional[UserItem] = None
170
+ self._location: Optional[LocationItem] = None
171
+ self._encrypt_extracts: Optional[bool] = None
172
+ self._default_view_id: Optional[str] = None
173
+ self._share_description: Optional[str] = None
174
+ self._last_published_at: Optional[datetime.datetime] = None
142
175
 
143
176
  return None
144
177
 
@@ -234,6 +267,14 @@ class WorkbookItem:
234
267
  def size(self):
235
268
  return self._size
236
269
 
270
+ @property
271
+ def sheet_count(self) -> Optional[int]:
272
+ return self._sheet_count
273
+
274
+ @property
275
+ def has_extracts(self) -> Optional[bool]:
276
+ return self._has_extracts
277
+
237
278
  @property
238
279
  def updated_at(self) -> Optional[datetime.datetime]:
239
280
  return self._updated_at
@@ -289,7 +330,7 @@ class WorkbookItem:
289
330
  return self._thumbnails_user_id
290
331
 
291
332
  @thumbnails_user_id.setter
292
- def thumbnails_user_id(self, value: str):
333
+ def thumbnails_user_id(self, value: Optional[str]):
293
334
  self._thumbnails_user_id = value
294
335
 
295
336
  @property
@@ -297,9 +338,37 @@ class WorkbookItem:
297
338
  return self._thumbnails_group_id
298
339
 
299
340
  @thumbnails_group_id.setter
300
- def thumbnails_group_id(self, value: str):
341
+ def thumbnails_group_id(self, value: Optional[str]):
301
342
  self._thumbnails_group_id = value
302
343
 
344
+ @property
345
+ def project(self) -> Optional[ProjectItem]:
346
+ return self._project
347
+
348
+ @property
349
+ def owner(self) -> Optional[UserItem]:
350
+ return self._owner
351
+
352
+ @property
353
+ def location(self) -> Optional[LocationItem]:
354
+ return self._location
355
+
356
+ @property
357
+ def encrypt_extracts(self) -> Optional[bool]:
358
+ return self._encrypt_extracts
359
+
360
+ @property
361
+ def default_view_id(self) -> Optional[str]:
362
+ return self._default_view_id
363
+
364
+ @property
365
+ def share_description(self) -> Optional[str]:
366
+ return self._share_description
367
+
368
+ @property
369
+ def last_published_at(self) -> Optional[datetime.datetime]:
370
+ return self._last_published_at
371
+
303
372
  def _set_connections(self, connections):
304
373
  self._connections = connections
305
374
 
@@ -342,6 +411,15 @@ class WorkbookItem:
342
411
  views,
343
412
  data_acceleration_config,
344
413
  data_freshness_policy,
414
+ sheet_count,
415
+ has_extracts,
416
+ project,
417
+ owner,
418
+ location,
419
+ encrypt_extracts,
420
+ default_view_id,
421
+ share_description,
422
+ last_published_at,
345
423
  ) = self._parse_element(workbook_xml, ns)
346
424
 
347
425
  self._set_values(
@@ -361,6 +439,15 @@ class WorkbookItem:
361
439
  views,
362
440
  data_acceleration_config,
363
441
  data_freshness_policy,
442
+ sheet_count,
443
+ has_extracts,
444
+ project,
445
+ owner,
446
+ location,
447
+ encrypt_extracts,
448
+ default_view_id,
449
+ share_description,
450
+ last_published_at,
364
451
  )
365
452
 
366
453
  return self
@@ -383,6 +470,15 @@ class WorkbookItem:
383
470
  views,
384
471
  data_acceleration_config,
385
472
  data_freshness_policy,
473
+ sheet_count,
474
+ has_extracts,
475
+ project,
476
+ owner,
477
+ location,
478
+ encrypt_extracts,
479
+ default_view_id,
480
+ share_description,
481
+ last_published_at,
386
482
  ):
387
483
  if id is not None:
388
484
  self._id = id
@@ -417,6 +513,24 @@ class WorkbookItem:
417
513
  self.data_acceleration_config = data_acceleration_config
418
514
  if data_freshness_policy is not None:
419
515
  self.data_freshness_policy = data_freshness_policy
516
+ if sheet_count is not None:
517
+ self._sheet_count = sheet_count
518
+ if has_extracts is not None:
519
+ self._has_extracts = has_extracts
520
+ if project:
521
+ self._project = project
522
+ if owner:
523
+ self._owner = owner
524
+ if location:
525
+ self._location = location
526
+ if encrypt_extracts is not None:
527
+ self._encrypt_extracts = encrypt_extracts
528
+ if default_view_id is not None:
529
+ self._default_view_id = default_view_id
530
+ if share_description is not None:
531
+ self._share_description = share_description
532
+ if last_published_at is not None:
533
+ self._last_published_at = last_published_at
420
534
 
421
535
  @classmethod
422
536
  def from_response(cls, resp: str, ns: dict[str, str]) -> list["WorkbookItem"]:
@@ -443,6 +557,12 @@ class WorkbookItem:
443
557
  created_at = parse_datetime(workbook_xml.get("createdAt", None))
444
558
  description = workbook_xml.get("description", None)
445
559
  updated_at = parse_datetime(workbook_xml.get("updatedAt", None))
560
+ sheet_count = string_to_int(workbook_xml.get("sheetCount", None))
561
+ has_extracts = string_to_bool(workbook_xml.get("hasExtracts", ""))
562
+ encrypt_extracts = string_to_bool(e) if (e := workbook_xml.get("encryptExtracts", None)) is not None else None
563
+ default_view_id = workbook_xml.get("defaultViewId", None)
564
+ share_description = workbook_xml.get("shareDescription", None)
565
+ last_published_at = parse_datetime(workbook_xml.get("lastPublishedAt", None))
446
566
 
447
567
  size = workbook_xml.get("size", None)
448
568
  if size:
@@ -452,14 +572,18 @@ class WorkbookItem:
452
572
 
453
573
  project_id = None
454
574
  project_name = None
575
+ project = None
455
576
  project_tag = workbook_xml.find(".//t:project", namespaces=ns)
456
577
  if project_tag is not None:
578
+ project = ProjectItem.from_xml(project_tag, ns)
457
579
  project_id = project_tag.get("id", None)
458
580
  project_name = project_tag.get("name", None)
459
581
 
460
582
  owner_id = None
583
+ owner = None
461
584
  owner_tag = workbook_xml.find(".//t:owner", namespaces=ns)
462
585
  if owner_tag is not None:
586
+ owner = UserItem.from_xml(owner_tag, ns)
463
587
  owner_id = owner_tag.get("id", None)
464
588
 
465
589
  tags = None
@@ -473,6 +597,11 @@ class WorkbookItem:
473
597
  if views_elem is not None:
474
598
  views = ViewItem.from_xml_element(views_elem, ns)
475
599
 
600
+ location = None
601
+ location_elem = workbook_xml.find(".//t:location", namespaces=ns)
602
+ if location_elem is not None:
603
+ location = LocationItem.from_xml(location_elem, ns)
604
+
476
605
  data_acceleration_config = {
477
606
  "acceleration_enabled": None,
478
607
  "accelerate_now": None,
@@ -505,6 +634,15 @@ class WorkbookItem:
505
634
  views,
506
635
  data_acceleration_config,
507
636
  data_freshness_policy,
637
+ sheet_count,
638
+ has_extracts,
639
+ project,
640
+ owner,
641
+ location,
642
+ encrypt_extracts,
643
+ default_view_id,
644
+ share_description,
645
+ last_published_at,
508
646
  )
509
647
 
510
648
 
@@ -535,3 +673,15 @@ def parse_data_acceleration_config(data_acceleration_elem):
535
673
  # Used to convert string represented boolean to a boolean type
536
674
  def string_to_bool(s: str) -> bool:
537
675
  return s.lower() == "true"
676
+
677
+
678
+ @overload
679
+ def string_to_int(s: None) -> None: ...
680
+
681
+
682
+ @overload
683
+ def string_to_int(s: str) -> int: ...
684
+
685
+
686
+ def string_to_int(s):
687
+ return int(s) if s is not None else None